如何在Fortran中使用命令行参数?

15

GCC版本4.6

问题:寻找一种从命令行中传递参数给可执行文件(例如a.out),更具体地说是传递一个双精度数数组的方法。

尝试:使用标准中较旧的READ(*,*)命令: 程序test.f -

PROGRAM MAIN  
     REAL(8)    :: A,B  
     READ(*,*) A,B
     PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN

执行 -

$ gfortran test.f
$ ./a.out 3.D0 1.D0

这没有起作用。经过一番反思,发现

$./a.out
3.d0,1.d0
   4.0000000000000000                0

这段代码可以工作,但第二行是一个输入提示符,而且无法在一行中完成目标。此外,COMMAND_ARGUMENT_COUNT() 显示,与 PERL 不同,输入提示符中输入的数字并不真正算作“命令行参数”。


2
你可以使用你的代码执行 'echo "3 1" | a.out'。虽然我不建议这样做,但也许这可以帮助你理解 read(*,*) 的作用。 - agentp
它确实有效!!谢谢@george。我之前尝试过./a.out | echo '3.1d0 1.d0',但它没有起作用。对于我的目的来说,这种方式更好,因为不需要指定格式,这似乎更自然和通用! - Debanjan Basu
你没有指定你正在编译的Fortran方言,但是这里有两种描述参数解析的方式,取决于Fortran的版本。 - user1934428
4个回答

26

如果你想要获取命令行传递给你的程序的参数,可以使用(自Fortran 2003以来)标准内置子例程GET_COMMAND_ARGUMENT。可能会像这样工作:

如果您想在程序启动时从命令行接收参数,请使用(自Fortran 2003开始引入的)标准内置子例程GET_COMMAND_ARGUMENT。以下示例可能有帮助:

PROGRAM MAIN  
     REAL(8)    :: A,B
     integer :: num_args, ix
     character(len=12), dimension(:), allocatable :: args

     num_args = command_argument_count()
     allocate(args(num_args))  ! I've omitted checking the return status of the allocation 

     do ix = 1, num_args
         call get_command_argument(ix,args(ix))
         ! now parse the argument as you wish
     end do

     PRINT*, A+B, COMMAND_ARGUMENT_COUNT()
END PROGRAM MAIN

注意:

  • 子程序 get_command_argument 的第二个参数是一个字符变量,您需要解析它并转换为实数或其他类型。请注意,我在args数组的每个元素中只允许使用12个字符,您可能需要微调。
  • 正如您已经发现的那样,在Fortran程序中,read不用于读取命令行参数。

由于您想读取一个实数数组,您可能最好使用您已经掌握的方法,也就是在程序启动之后从终端读取它们,这取决于您自己。


另一种方法当然是从ASCII或namelist输入文件中读取数字数组,其文件名作为命令行参数传递。 - sigma
感谢您的精彩回答@High Performance Mark...您能否添加一个部分或链接到一个页面,提供有关如何设置读取格式的信息? 我无法区分哪些是旧的,哪些是新的! 在Fortran 2003中,READ(*,*)是解析应该完成的方式吗? - Debanjan Basu
1
在等待我的回复评论的时候,可以试试使用谷歌。 - High Performance Mark
我找到了这个……但在我的gfortran版本上无法编译! 编辑:它可以。显然更改文件名会影响程序的内容。再次感谢! 鞠躬 - Debanjan Basu
read (*,*) 是标准的Fortran语法,用于从标准输入进行列表式读取。大多数操作系统将标准输入连接到终端。尽管这个语法已经存在很长时间了,但它并没有任何问题。关于Fortran 95特性的好网页:http://en.wikipedia.org/wiki/Fortran_95_language_features - M. S. B.

1

READ (*,*) 的作用是从标准输入读取数据,例如通过键盘输入的字符。

正如问题所示,COMMAND_ARGUMENT_COUNT() 可以用来获取命令行参数的数量。

High Performance Mark 给出的答案展示了如何使用 GET_COMMAND_ARGUMENT() 将由空格分隔的单个命令行参数检索为单个字符字符串。您还可以使用 GET_COMMAND() 获取整个命令行。然后,您需要将基于字符的信息解析为程序中的数据。

在非常简单的情况下,您只需要程序需要的两个数字,因此您可以从 arg 1 读取一个数字,从 arg 2 读取另一个数字。这很简单。或者,如果它们是逗号分隔的,您可以从单个参数中读取三个数字,例如 1,2,3,使用简单的 read(arg,*) nums(1:3)

对于一般复杂的命令行解析,人们使用诸如 Hani 答案中提到的库。您需要设置它们,以便库知道命令行参数的预期语法和应该填充值的数据。

有一个折中的方法,它仍然相对简单,但已经有多个参数,对应于程序中的Fortran变量,可能存在也可能不存在。在这种情况下,可以使用名称列表来进行语法和解析。

以下是一个示例,关键点是名称列表/cmd/名称、点、标志

     implicit none
     
     real :: point(3)
     logical :: flag
     character(256) :: name
     character(1024) :: command_line
     
     call read_command_line
     
     call parse_command_line
     
     print *, point
     print *, "'",trim(name),"'"
     print *, flag
     
contains

     subroutine read_command_line
       integer :: exenamelength
       integer :: io, io2
       
       command_line = ""
       call get_command(command = command_line,status = io)
       if (io==0) then
         call get_command_argument(0,length = exenamelength,status = io2)
         if (io2==0) then
           command_line = "&cmd "//adjustl(trim(command_line(exenamelength+1:)))//" /"
         else
           command_line = "&cmd "//adjustl(trim(command_line))//" /"
         end if
       else
         write(*,*) io,"Error getting command line."
       end if
     end subroutine
     
     subroutine parse_command_line
       character(256) :: msg
       namelist /cmd/ name, point, flag
       integer :: io

       if (len_trim(command_line)>0) then
         msg = ''
         read(command_line,nml = cmd,iostat = io,iomsg = msg)
         if (io/=0) then
           error stop "Error parsing the command line or cmd.conf " // msg
         end if
       end if
     end subroutine
end

在Bash中的用法:

> ./command flag=T name=\"data.txt\" point=1.0,2.0,3.0
   1.00000000       2.00000000       3.00000000    
 'data.txt'
 T

或者

> ./command flag=T name='"data.txt"' point=1.0,2.0,3.0
   1.00000000       2.00000000       3.00000000    
 'data.txt'
 T

对于字符串中的引号进行转义是不幸必要的,因为bash会吃掉第一个引号。


0

最简单的方法是使用库。有FLAPf90getopt可用。两者都是开源的,且在自由许可下授权。

后者是由Mark Gates和我编写的,只有一个模块,可以在几分钟内学会,但包含了解析GNU-和POSIX-like命令行选项所需的所有内容。前者更为复杂,甚至可以在闭源项目中使用。请查看它们。

此外,还有https://fortranwiki.org/fortran/show/Command-line+arguments上的库。


1
公平披露你是链接仓库的维护者。 - Vladimir F Героям слава
我写了。我写道:“后者是由马克·盖茨和写的,…” - Hani
好的,我认为这并没有真正回答问题,或者说是一个仅包含链接的答案。可能有其他更直接关于解析的问题。最近在Fortran讨论中有一个关于此的线程。我很少在那里写东西,但这次我写了https://fortran-lang.discourse.group/t/specifying-command-line-arguments-via-the-program-statement/2982 我可能会用那段代码写一个答案。 - Vladimir F Героям слава

0

函数在什么意义上与F77兼容?它不使用任何特殊功能,因此可以从F77中调用。因此,在这个意义上,它确实是兼容的。但它是一种非标准扩展,因此并非所有Fortran 77(或任何后续标准版本)编译器都可用。 - Vladimir F Героям слава

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接