Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 206
Pythonist Avatar asked Feb 02 '16 12:02

Pythonist


2 Answers

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 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".

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.

like image 132
francescalus Avatar answered Sep 28 '22 10:09

francescalus


I will leave this for completeness, it may be useful to somebody, but francescalus' answer is much better.

Basically, just read it with some default length, check the STATUS variable and if it did not fit, try again.

From the gcc manual:

If the argument retrieval fails, STATUS is a positive number; if VALUE contains a truncated command line argument, STATUS is -1; and otherwise the STATUS is zero.

So:

character(len=:), allocatable :: argument
integer :: stat, n

n = 100
allocate(character(n) :: argument)

do
  call GET_COMMAND_ARGUMENT(1,argument, status=stat)
  if (stat>=0) exit
  deallocate(argument)
  n = n * 2
  allocate(character(n) :: argument)
end do

if (stat>0) error...

argument = trim(argument)

print*, argument
like image 44
Vladimir F Героям слава Avatar answered Sep 28 '22 10:09

Vladimir F Героям слава