How to use command line arguments in Fortran?
Asked Answered
P

4

18

GCC version 4.6

The Problem: To find a way to feed in parameters to the executable, say a.out, from the command line - more specifically feed in an array of double precision numbers.

Attempt: Using the READ(*,*) command, which is older in the standard: Program test.f -

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

The execution -

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

This did not work. On a bit of soul-searching, found that

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

does work, but the second line is an input prompt, and the objective of getting this done in one-line is not achieved. Also the COMMAND_ARGUMENT_COUNT() shows that the numbers fed into the input prompt don't really count as 'command line arguments', unlike PERL.

Pullet answered 12/12, 2012 at 16:6 Comment(3)
you could do 'echo "3 1" | a.out' with your code. Not that I'd advise it but maybe this helps you understand what read(*,*) does.Channel
It does work!! thank you @george . I did try ./a.out | echo '3.1d0 1.d0'earlier .. and it didn't work. And it is better for my purposes because this way a format need not be specified, which appears more natural .. and general to me!Pullet
You did not specify for which Fortran dialect you are compiling, but here are two ways to describe the argument parsing, depending on the Fortran version.Secede
C
27

If you want to get the arguments fed to your program on the command line, use the (since Fortran 2003) standard intrinsic subroutine GET_COMMAND_ARGUMENT. Something like this might work

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

Note:

  • The second argument to the subroutine get_command_argument is a character variable which you'll have to parse to turn into a real (or whatever). Note also that I've allowed only 12 characters in each element of the args array, you may want to fiddle around with that.
  • As you've already figured out read isn't used for reading command line arguments in Fortran programs.

Since you want to read an array of real numbers, you might be better off using the approach you've already figured out, that is reading them from the terminal after the program has started, it's up to you.

Clyde answered 12/12, 2012 at 16:21 Comment(5)
Another way is of course to read the number array from an ASCII or namelist input file whose name you pass as a command line argument.Evalyn
thank you for your awesome answer @High Performance Mark ... could you please add a section, or link to a page, with information on how to set the read formats? Can't differentiate which ones are old, and which one are new! Is READ(*,*) how the parsing is supposed to be done in Fortran 2003?Pullet
While you wait for my response to your comment, try Google.Clyde
did find this ... but does not compile on my version of gfortran! EDIT: it does. apparently changing the filename affects the contents of the program. Thank you again! bowsPullet
read (*,*) is standard Fortran for a list-directed read from standard input. Most operating systems connect standard input to the terminal. There is nothing wrong with it even though it has been in the language a long time. A good webpage for Fortran 95 features: en.wikipedia.org/wiki/Fortran_95_language_featuresCultivator
E
1

What READ (*,*) does is that it reads from the standard input. For example, the characters entered using the keyboard.

As the question shows COMMAND_ARGUMENT_COUNT() can be used to get the number of the command line arguments.

The accepted answer by High Performance Mark show how to retrieve the individual command line arguments separated by blanks as individual character strings using GET_COMMAND_ARGUMENT(). One can also get the whole command line using GET_COMMAND(). One then has to somehow parse that character-based information into the data in your program.

I very simple cases you just need the program requires, for example, two numbers, so you read one number from arg 1 and another form arg 2. That is simple. Or you can read a triplet of numbers from a single argument if they are comma-separated like 1,2,3 using a simple read(arg,*) nums(1:3).

For general complicated command line parsing one uses libraries such as those mentioned in the answer by Hani. You have set them up so that the library knows the expected syntax of the command line arguments and the data it should fill with the values.

There is a middle ground, that is still relatively simple, but one already have multiple arguments, that correspond to Fortran variables in the program, that may or may not be present. In that case one can use the namelist for the syntax and for the parsing.

Here is an example, the man point is the namelist /cmd/ name, point, flag:

     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

Usage in bash:

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

or

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

Escaping the quotes for the string is unfortunately necessary, because bash eats the first quotes.

Eternity answered 3/4, 2022 at 11:17 Comment(0)
A
0

The easiest way is to use a library. There is FLAP or f90getopt available. Both are open source and licensed under free licenses.

The latter is written by Mark Gates and me, just one module and can be learned in minutes but contains all what is needed to parse GNU- and POSIX-like command-line options. The first is more sophisticated and can be used even in closed-source projects. Check them out.

Furthermore libraries at https://fortranwiki.org/fortran/show/Command-line+arguments

Aschim answered 2/4, 2022 at 19:11 Comment(3)
It is fair to disclose that you are the maintainer of the linked repo.Chlorine
I did. I wrote "The latter is written by Mark Gates and me , ..."Aschim
OK, I do not think this actually answers the question or is somewhat a link-only answer. There might be other question more directly about the parsing. There was a thread on Fortran discourse recently about this. I rarely write there, but I did this time fortran-lang.discourse.group/t/… I might actually write an answer with that code.Chlorine
I
0

The accepted answer is correct but you can also use https://gcc.gnu.org/onlinedocs/gfortran/GETARG.html This function is compatible with F77.

PROGRAM test_getarg
  INTEGER :: i
  CHARACTER(len=32) :: arg

  DO i = 1, iargc()
    CALL getarg(i, arg)
    WRITE (*,*) arg
  END DO
END PROGRAM

related to sample code https://gcc.gnu.org/onlinedocs/gfortran/IARGC.html

Isogamy answered 21/4, 2023 at 8:54 Comment(1)
In what sense is the function compatible with F77? It does not use any special features so it is callable from F77. So in this sense it is indeed compatible. But it is a non-standard extension so it is not available in all Fortran 77 (or any later standard version) compilers.Chlorine

© 2022 - 2025 — McMap. All rights reserved.