如何使用 Fortran 获取可执行文件的位置?

发布于 2024-09-28 21:36:49 字数 310 浏览 1 评论 0原文

我有一个用 Fortran 90/95 编写的程序;调用后它总是读取某个数据文件。为了用户的方便,我希望他们只需将此文件放在与可执行文件本身相同的目录中,而不需要他们设置一些环境变量/扩展 $PATH,并且不强迫他们为此目的使用某个目录。程序应该“简单地”在其本身存储的目录中查找文件,而不是在其运行的目录中查找。然而到目前为止,我还没有找到解决这个问题的办法。我尝试使用

getarg(0,path)

,但这只给了我用来调用程序的任何字符串,而不是它的绝对路径。

如果您有任何建议以及解决方法,请随时回复。预先非常感谢!

I have a program written in Fortran 90/95; after invocation it always reads a certain data file. For the users' convenience I'd like them to just have to place this file in the same directory as the executable itself, without making them set some environment variable/extend $PATH, and without forcing them to use a certain directory for that purpose. The program should 'simply' look for the file in the directory in which it itself is stored, NOT in the directory from which it is run. So far, however, I have failed to find a solution to this problem. I tried using

getarg(0,path)

but that only gave me whatever string I had used to invoke the program, not its absolute path.

If you have any suggestions, also concerning workarounds, please don't hesitate to reply. Thanks a lot in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

深海不蓝 2024-10-05 21:36:49

要求二进制文件位于某个特定目录中让我觉得很奇怪。只要在 PATH 中找到二进制文件,就应该可以工作。您可以做的是尝试从当前工作目录(即用户启动程序时所在的目录)读取数据文件。

我您不想要求用户始终复制周围的数据文件,您可以搜索一些“默认”位置,然后使用找到该文件的第一个位置,例如当前工作目录,然后使用 $HOME/.your_program/ file.dat,最后是 /usr/local/share/your_program_name/file.dat,或类似的东西。

编辑 但是,如果您希望继续走这条错误的道路,至少在 Linux 上您可以使用 readlink() (您可能需要为此创建一个 C 包装器,请参阅最近的 ISO C BINDING Fortran 编译器)检查 /proc/self/exe 符号链接。

顺便说一句,GETARG 不是 Fortran 标准的一部分,因此您依赖于供应商扩展(诚然,该扩展得到了广泛的支持)。从 Fortran 2003 开始​​,执行此操作的标准功能是 GET_COMMAND_ARGUMENT 内在函数。

Requiring that the binary is in some specific directory strikes me as weird. As long as the binary is found on the PATH, stuff should work. What you can do instead is to try to read the data file from the current working directory, i.e. the directory the user is in when launching the program.

I you don't want to require users to always copy the data file around you could search a few "default" places and then use the first one where the file is found, e.g. the current working directory, then $HOME/.your_program/file.dat, and finally /usr/local/share/your_program_name/file.dat, or something like that.

Edit If you, however, wish to continue down this wrongheaded path, at least on Linux you can use readlink() (you probably need to create a C wrapper for this, see the ISO C BINDING in recent Fortran compilers) to check the /proc/self/exe symlink.

As an aside, GETARG is not part of the Fortran standard, so you're relying on a vendor extension (which admittedly is quite widely supported). As of Fortran 2003, the standard feature for doing this is the GET_COMMAND_ARGUMENT intrinsic.

鹿港小镇 2024-10-05 21:36:49

我认为这是在 fortran 中解决的一个难题,并且我认为您的方法是正确的(将数据文件与可执行文件放在同一目录中)。 Fortran 需要类似于 python 的东西

os.path.dirname(os.path.realpath(sys.argv[0]))

,不幸的是,Fortran 对于 I/O 来说绝对是糟糕的,而你的问题只突出了一个小方面。

以防万一其他人突然认为这是一个微不足道的问题 - 考虑一下您不知道人们将在其上运行此可执行文件的操作系统,并且您不知道可执行文件的名称的情况。

我的(可能很糟糕)解决方法是使用 INDEX 来查找“/”。如果它返回非零,那么用户“必须”位于 Linux 系统上,因此使用 INDEX 去掉可执行文件的名称,您就得到了路径。然后查找“\”,如果 INDEX 找到某些内容,则假定 OS=windows 并删除可执行文件。

I think this is a difficult problem to solve in fortran, and I think you're right in your approach (putting the data file in the same directory as the executable). Fortran needs something along the lines of python's

os.path.dirname(os.path.realpath(sys.argv[0]))

Unfortunately, fortran is absolutely terrible for I/O and your question highlights just one small facet.

Just in case someone else jumps the gun and thinks this is a trivial question - consider the case where you don't know the OS that people are going to run this executable on, and you don't know the executable's name.

My, probably awful, workaround is to use INDEX to look for "/". If it returns nonzero then the user "must" be on a linux system, so use INDEX to strip away the executable's name and you've got your path. Then look for "\", and if INDEX finds something then assume OS=windows and strip away the executable.

花开雨落又逢春i 2024-10-05 21:36:49

canavanin,您同时找到解决方案了吗?在 g77 中 getarg(0,path) 提供带有完整路径的可执行文件,但在 gfortran 中则不然。然而,gfortran 的这个缺点似乎只是 Windows 特有的。 参见此处演示了 getarg 在 bash shell 中的功能。 ---

更正:
我现在发现问题不是 GFORTRAN 与 G77 的问题,而是具体的构建。 DJGPP(DOS 端口)发行版的最新 GCC / GFORTRAN 版本 (4.54) 使 getarg(0,path) 在 [executable].exe 前面提供完整路径。分隔符是“/”而不是“\”。

canavanin, have you found a solution in the meantime? In g77 getarg(0,path) delivers the executable with the full path, but not in gfortran. However, this shortcoming of gfortran seems to be windows specific only. See Here which demonstrates getarg's functioning in a bash shell...

---corection:
I have found out now that the problem is not GFORTRAN vs G77, it's the specific build. The most recent GCC / GFORTRAN version (4.54) of the DJGPP (DOS port) distribution makes the getarg(0,path) deliver the full path in front of the [executable].exe. The separation character is "/" not "\".

北方的巷 2024-10-05 21:36:49

如果您使用的是 POSIX 兼容系统,则可以调用 realpath (或 readlink)C 函数来解析链接的目标 /proc/self/ exe,它始终指向当前正在运行的程序。
因此,您可以

full_path_to_current_program = real_path('/proc/self/exe')

real_path 只是 POSIX 函数的包装器。

实际的实现很简单但很冗长,主要是由于字符转换。假设 use iso_c_binding 在范围内,这有效:

function real_path(path)
   character(*), intent(in) :: path
   character(:), allocatable :: real_path

   interface
      type(c_ptr) function realpath_c(path, resolved_path) bind(c, name='realpath')
         use iso_c_binding
         implicit none
         type(c_ptr), value :: path, resolved_path
      end function

      subroutine free_c(ptr) bind(c, name='free')
         use iso_c_binding
         implicit none
         type(c_ptr), value :: ptr
      end subroutine
   end interface
   character(c_char), allocatable, target :: path_c(:)
   type(c_ptr) :: resolved_path_c

   path_c = str_f2c(path)
   resolved_path_c = realpath_c(c_loc(path_c), c_null_ptr)
   real_path = str_c2f(resolved_path_c)
   call free_c(resolved_path_c)
end function

function str_f2c(str) result(str__c)
   character(*), intent(in) :: str
   character(kind=c_char) :: str__c(len(str) + 1)

   integer :: i

   str__c = [character(kind=c_char) :: (str(i:i), i = 1, len(str)), c_null_char]
end function

function str_c2f(c_str) result(f_str)
   type(c_ptr), intent(in) :: c_str
   character(:), allocatable :: f_str

   interface
      integer(c_size_t) function strlen_c(s) bind(c, name='strlen')
         use iso_c_binding
         implicit none
         type(c_ptr), value :: s
      end function
   end interface
   character(kind=c_char), pointer :: f_char_array(:)
   integer :: i, n

   n = int(strlen_c(c_str))
   call c_f_pointer(c_str, f_char_array, [n])
   allocate(character(n) :: f_str)
   do i = 1, n
      f_str(i:i) = f_char_array(i)
   enddo
end function

If you are on a POSIX-compatible system, you may call the realpath (or readlink) C function to resolve the target of the link /proc/self/exe, which always points to the current program being run.
So you would do

full_path_to_current_program = real_path('/proc/self/exe')

where real_path is just a wrapper around a POSIX function.

The actual implementation is straightforward but verbose, mostly due to the character conversions. Assuming use iso_c_binding is in scope, this works:

function real_path(path)
   character(*), intent(in) :: path
   character(:), allocatable :: real_path

   interface
      type(c_ptr) function realpath_c(path, resolved_path) bind(c, name='realpath')
         use iso_c_binding
         implicit none
         type(c_ptr), value :: path, resolved_path
      end function

      subroutine free_c(ptr) bind(c, name='free')
         use iso_c_binding
         implicit none
         type(c_ptr), value :: ptr
      end subroutine
   end interface
   character(c_char), allocatable, target :: path_c(:)
   type(c_ptr) :: resolved_path_c

   path_c = str_f2c(path)
   resolved_path_c = realpath_c(c_loc(path_c), c_null_ptr)
   real_path = str_c2f(resolved_path_c)
   call free_c(resolved_path_c)
end function

function str_f2c(str) result(str__c)
   character(*), intent(in) :: str
   character(kind=c_char) :: str__c(len(str) + 1)

   integer :: i

   str__c = [character(kind=c_char) :: (str(i:i), i = 1, len(str)), c_null_char]
end function

function str_c2f(c_str) result(f_str)
   type(c_ptr), intent(in) :: c_str
   character(:), allocatable :: f_str

   interface
      integer(c_size_t) function strlen_c(s) bind(c, name='strlen')
         use iso_c_binding
         implicit none
         type(c_ptr), value :: s
      end function
   end interface
   character(kind=c_char), pointer :: f_char_array(:)
   integer :: i, n

   n = int(strlen_c(c_str))
   call c_f_pointer(c_str, f_char_array, [n])
   allocate(character(n) :: f_str)
   do i = 1, n
      f_str(i:i) = f_char_array(i)
   enddo
end function
痞味浪人 2024-10-05 21:36:49

在Digital Visual Fortran(等)中,在Windows中,我这样做:

  SUBROUTINE GetFullExeName(FULLNAME,L)  


!******************************************************************************  
!  
!  Gets the full name of the current executing program.  
!  
!******************************************************************************  
      USE DFWIN  
      CHARACTER*(*) FULLNAME          ! full name  
      INTEGER       L                 ! length  
      L= GetModuleFileName(NULL,FULLNAME,LEN(FULLNAME))   ! windows API  
      FULLNAME(L+1:) = ' '  
      END  

In Digital Visual Fortran (etc.), in Windows, I do it like this:

  SUBROUTINE GetFullExeName(FULLNAME,L)  


!******************************************************************************  
!  
!  Gets the full name of the current executing program.  
!  
!******************************************************************************  
      USE DFWIN  
      CHARACTER*(*) FULLNAME          ! full name  
      INTEGER       L                 ! length  
      L= GetModuleFileName(NULL,FULLNAME,LEN(FULLNAME))   ! windows API  
      FULLNAME(L+1:) = ' '  
      END  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文