I would like to have a program read from standard in by default or from a file if a specified. I have thought about using unit 5 for input, which I can redirect to a file in an open statement, but I understand 5 isn’t designated as standard input by the Fortran standard. Is that really a problem? Am I likely to run in to users where unit 5 is not standard in? I mean, someone with a Linux, WIndows or Mac machine. I don’t care about anything older than f2c. The program is used by random unsophisticated users, I’d like to put the redirection in the program rather than rely on a shell script I would have to explain.
As it is, the best I can think of to do that fits the standard is to replace each read statement with 2 read statements, one with a * and one with a unit number. Is that the best I can do?
I notice that some compilers such as Oracle Fortran allow a * in the open statement:
[UNIT=] u
u is an integer expression or an asterisk (*) that specifies the unit number. u is required
but does that modify read or write statements? I suppose it doesn’t matter, since so few compilers support this. Still, it seems odd if there is no way to redirect unit *.
If you have not used INPUT_UNIT before, here is an example that reads one line from stdin
and echoes it unless a filename is given on the command line, in which case that file is read from instead. This assumes your input file is a text file; reading binary files from stdin is problematic.
program readfrom
use,intrinsic :: iso_fortran_env, only : &
& stdin=>input_unit, &
& stdout=>output_unit,&
& stderr=>error_unit
implicit none
integer :: count, argument_length, lun, ios
character(len=:),allocatable :: filename
character(len=256) :: line, msg
! get number of arguments
count = command_argument_count()
if(count.ne.0)then
call get_command_argument(number=1,length=argument_length)
! allocate string array big enough to hold command line argument
allocate(character(len=argument_length) :: filename)
call get_command_argument(1, filename)
open(file=filename,newunit=lun)
else
lun=stdin
endif
read(lun,'(a)',iostat=ios,iomsg=msg)line
if(ios.eq.0)then
write(*,*)'READ :',trim(line)
else
write(*,*)'<ERROR>',trim(msg)
endif
end program readfrom`
so if you entered “readfrom myfile” it would read the first line of file “myfile”; otherwise it would
read a line from stdin. You do not have to rename the variables, but I prefer the names stdin, stdout, and stderr myself.
If you are using Intel Fortran and the ISO_FORTRAN_ENV constants, be sure to compile with -standard-semantics. If you don’t, the compiler will use various negative unit numbers for unit * (or PRINT).
No - my comment related to @feenberg 's mention of unit *, which is part of the standard language. Specifically, if you expected to open unit INPUT_UNIT and then do READ(*) and have that be the unit you opened, it would not work in Intel Fortran without the standard-semantics option (or -assume noold_unit_star)