Search code examples
arraysstringfortran

How to get command line arguments of unknown length in Fortran?


I'd like to read in some text strings from the command line used to run a program. I'm using the internal subroutine GET_COMMAND_ARGUMENT in a program that basically is something like:

program test
character(len=100) :: argument
call GET_COMMAND_ARGUMENT(1,argument)
print*, argument
end program test

The issue here is that I feel it's a bit dangerous to set the maximum length of the string at compilation time. Some of the arguments are typically files with their path, so they might be very long. A solution involving setting the length statically to 1000 sounds like an ugly workaround.

Isn't there a more elegant way in Fortran to define a string able to contain a chain of characters whose length is only known at run time?


Solution

  • It is possible to use what are called deferred-length character variables. These are not of a fixed constant length and their use can be found in questions such as a related one about data input.

    However, even with a deferred length variable used like (for this is the syntax)

    character(len=:), allocatable :: argument
    allocate(character(some_length) :: argument)  ! For integer some_length
    call GET_COMMAND_ARGUMENT(1,argument)
    print*, argument
    end
    

    one still has to worry about what some_length should be. If we just choose 100, we're back to where we were.

    We have to worry about this because get_command_argument as is currently widely implemented doesn't take such a deferred length argument and allocate it to the desired length. That is

    character(len=:), allocatable :: argument
    call GET_COMMAND_ARGUMENT(1,argument)  ! Will argument be allocated in the subroutine?
    print*, argument
    end
    

    offers the answer "no".1

    Coming, then, to a way to handle this, we look at other (optional) arguments to get_command_argument. In particular, there is one called length:

    character(len=:), allocatable :: argument
    integer arglen
    call GET_COMMAND_ARGUMENT(1,length=arglen)
    allocate(character(arglen) :: argument)
    call GET_COMMAND_ARGUMENT(1,value=argument)
    print*, argument
    end
    

    Naturally, one could create a wrapper subroutine which did take an allocatable deferred length character variable and did all that work.


    1 Fortran 2023 changes the behaviour of intrinsic procedures which take character variables. get_command_argument, along with many others, in this language revision does now allocate such a deferred-length character. Where the answer was "no" before Fortran 2023 is now "yes". Practically, though, we'll be writing portable Fortran code assuming the answer is "no" for many years yet.