genomeek
One little detail that can make your program looks professional is
the ability to pass arguments on the command line.
Surprisingly a lot of Fortran programmers don’t know that this can be done easily, this post will give some tips to use commands arguments.
First, until recently Fortran standard didn’t include any instructions to handle command line argument. Nevertheless, several compilers (in fact all the compilers, I’ve worked with) like ifort, gfortran, g95 were accepting the use of C function iargc and the subroutine getarg. And I must confess that until recently this was the tools I used.
Anyways since more and more fortran compilers are compliant with the F03 standard, the official (and recommended) way to deal with command line arguments is the function :
command_argument_count()
which retrieve the number of argument passed to the command line
and the subroutine
get_command_argument(i,aname)
which put character string present at the “i”th argument into variable “aname”.
First step
To illustrate use of command arguments let’s make a sample program
program TestArg integer::narg,cptArg !#of arg & counter of arg character(len=20)::name !Arg name !Check if any arguments are found narg=command_argument_count() !Loop over the arguments if(narg>0)then !loop across options do cptArg=1,narg call get_command_argument(cptArg,name) select case(adjustl(name)) case("--help","-h") write(*,*)"This is program TestArg : Version 0.1" case default write(*,*)"Option ",adjustl(name),"unknown" end select end do end if end program TestArg
ifort TestArg.f90 -o TestArg ; ./TestArg --help
This is for the basic case. A good hint is trying to use “classical” flags, in fact many software use somehow the same flag for classical purpose “verbosity”, “help” etc… I do believe that the arguments are really helpful because they allow you to offer a variety of options that won’t bother the user by default. In fact, without optional arguments, we tend to ask the user a lot of information that are for common users either obvious or useless.
A convenient use of the command arguments, is to create a debug flag that will help to correct your program by enabling the printing of more information, or version flag that can tell you which version of the software are you using…these flags can reduce tremendously your debugging time !
Adding some difficulties….
I generally use arg to activate an option, and later in the program I execute some pieces of code to be executed. As instance, if I want to write down a certain file to be read, I add a flag and later in the program execution, I will ask for the name of this file. Anyway, you probably know some software (plink, beagle as instance) which looks for all the information they need in the command arguments : With plink, you would as instance write :
plink --ped mydata.ped --map autosomal.map
A program such as the previous one, won’t handle such situation…but be can program something similar (although I believe this is not so nice).
program TestComplexArg integer::narg,cptArg character(len=20)::name,pedFile,mapFile logical::lookForPed=.FALSE. logical::lookForMap=.FALSE. logical::fileExist !Check if arguments are found narg=command_argument_count() if(narg>0)then !loop across options do cptArg=1,narg call get_command_argument(cptArg,name) select case(adjustl(name)) !First known args case("--ped") lookForPed=.TRUE. !change logical value case("--map") lookForMap=.TRUE. case default !Treat the second arg of a serie if(LookForPed)then pedFile=adjustl(name) !assign a value to pedfile inquire(file=pedFile,exist=fileExist)!check if it exist if(.not.fileExist)then write(*,*)'file ',pedFile,' not found' stop endif LookForPed=.FALSE. !put the logical variable to its initial value elseif(LookForMap)then mapFile=adjustl(name) inquire(file=mapFile,exist=fileExist) if(.not.fileExist)then write(*,*)'file ',mapFile,' not found' stop endif LookForMap=.FALSE. else write(*,*)"Option ",adjustl(name),"unknown" endif end select end do endif end program TestComplexArg
#Compile ifort TestArg.f90 -o TestArg #Create empty files touch mydata.ped autosomal.map #Test ./TestArg --ped mydata.ped --map autosomal.map
I hope these examples can be useful, and allow you to code “better” (either less verbose or more user-friendly) software.
Using Linux commands in Fortran
Today, we’re going to talk about the system procedure in Fortran and how it can be used to spice up your Fortran code with Linux command line magic. In Fortran, the system procedure allows you to execute shell commands from within your program, opening up a whole new world of possibilities. Now, I know what you’re thinking. “Fortran and Linux command line? That sounds like mixing oil and water!” What better way to demonstrate this than by shuffling an array of integers using the seq and shuf commands?
So, let’s get started with our little shuffling exercise. We are going to use the seq command to generate the sequence of integers and pipe the output to the shuf command.
For those who are not familiar with seq and shuf commands, you can try the following commands on your terminal
The first command prints the integers 1 to 10, and in the next command, shuf generates random permutations of its input lines (in our case the input passed is the output of seq 1 10 ).
To execute this command from within Fortran, we use the system procedure and pass the command as a string. Here’s the Fortran code
PROGRAM shuffle_array IMPLICIT NONE INTEGER, PARAMETER :: N = 100 CHARACTER(LEN=100) :: cmd ! write the command in the string 'cmd' WRITE(cmd,'(a,i5,a)') 'seq 1 ', N, ' | shuf > shuffled_array.txt' ! call the 'system' procedure with the 'cmd' as the argument CALL SYSTEM(TRIM(cmd)) END PROGRAM shuffle_array
that you can compile using
and execute the program using
After running the code, you will see the sequence 1…100 randomly permuted in the file “shuffled_array.txt”. You can read the new shuffled array to shuffle molecular coordinates or files, etc.
This is just a silly little example, but the system procedure can be incredibly useful for more complex tasks. You can use it to run external programs, automate tasks, or even create entire pipelines of shell commands.
So, don’t be afraid to experiment with the system procedure and see what kind of crazy concoctions you can come up with. Just remember to keep your code safe and always sanitize your input!
That’s all for now, folks. Happy coding and may the Fortran be with you!