Test whether a directory exists or not - fortran

I'm trying to verify that a directory exists using Fortan90. On various sites I found:
logical :: dir_e
inquire(file='./docs/.', exist=dir_e)
if ( dir_e ) then
write(*,*) "dir exists!"
else
! workaround: it calls an extern program...
call system('mkdir docs')
end if
However, inquire returns False whether or not the directory exists and if I execute this code twice, I get an error message
cannot make dir, file already exists
If I use:
inquire(file='./docs/test', exist=dir_e)
with an existing file test, inquire returns true.
How can I check for the existence of a directory? I am using ubuntu 11.04 and the ifort compiler.

The Fortran standard 95, 2003 and 2008 do not specify, how inquire should treat directories. From my experience under Linux, gfortran treats them as files, ifort does not. The directory statement is a proprietary feature of ifort and should therefore be avoided.
The safest would be to test for a file in the said directory.

The following should work:
INQUIRE (DIRECTORY=dir, EXIST=ex [, DIRSPEC=dirspec] [, ERR=label] [, IOSTAT=i-var] )
I don't have ifort on this machine so I can't test it.
Addendum: The code posted originally works with gfortran. The DIRECTORY statement works with ifort but not with gfortran.
And in case for more information check: http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/win/compiler_f/lref_for/source_files/rfinquir.htm#rfinquir

Most of the time, one checks if the directory exists so to write something in it. What I do is just create the directory. If it already exists there is no problem.
CALL system("mkdir video")
CALL chdir("video")
CALL getcwd(path)

You could use C routines to test the files :
C side (OK with ifort and gfortran on Win32 and Linux 32/64)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#if defined(_WIN32) && defined(__INTEL_COMPILER)
# include "dirent_windows.h"
#else
# include <dirent.h>
#endif
void file_info(const char*filename,int*mode,int*exist,int*time){
int k;
struct stat buf;
k=stat(filename,&buf);
if(k != 0) {
*mode=0;
*exist=0;
*time=0;
}else{
*mode=buf.st_mode;
if(*mode == 0) *exist=0; else *exist=1;
*time=buf.st_mtime;
}
}
Fortran side :
MODULE file
USE iso_c_binding
INTERFACE
SUBROUTINE file_info(filename,mode,exist,time) BIND(C,name="file_info")
USE iso_c_binding
CHARACTER(kind=C_CHAR),INTENT(in) :: filename(*)
INTEGER(C_INT),INTENT(out) :: mode,exist,time
END SUBROUTINE
END INTERFACE
END MODULE
How to use in a Fortran routine :
..
use file
use iso_c_binding
...
integer(c_int) :: mode,exist,time
...
call file_info("./docs"//char(0),mode,exist,time)
Advantage : it works for any kind of file and provides additional pieces of information like the mode (read/write/execute permission) and the creation time.

Here's a subroutine I use often -- it uses the conditional you asked about:
subroutine create_directory( newDirPath )
! Author: Jess Vriesema
! Date: Spring 2011
! Purpose: Creates a directory at ./newDirPath
implicit none
character(len=*), intent(in) :: newDirPath
character(len=256) :: mkdirCmd
logical :: dirExists
! Check if the directory exists first
! inquire( file=trim(newDirPath)//'/.', exist=dirExists ) ! Works with gfortran, but not ifort
inquire( directory=newDirPath, exist=dirExists ) ! Works with ifort, but not gfortran
if (dirExists) then
! write (*,*) "Directory already exists: '"//trim(newDirPath)//"'"
else
mkdirCmd = 'mkdir -p '//trim(newDirPath)
write(*,'(a)') "Creating new directory: '"//trim(mkdirCmd)//"'"
call system( mkdirCmd )
endif
end subroutine create_directory
Depending on which compiler you use, you'll have to decide which of those conditionals is right for you.
Unfortunately, I do not have access to nagfor and don't know how it treats directories.

Another non-portable solution is to let the shell (Bash, in this case) do the work:
call system('[[ ! -e docs ]] && mkdir docs')

I had the same problem. If you want a compiler independent way of doing this, you can try to open a small file within the directory. The open statement allows for the code to jump to a particular line (specified by err=) if the open statement fails:
! Tests whether the directory exists
subroutine checkdir(dir)
implicit none
character(len=*), intent(in) :: dir
integer :: unitno
! Test whether the directory exists
open(newunit=unitno,file=trim(dir)//'deleteme.txt',status='replace',err=1234)
close (unitno)
return
! If doesn't exist, end gracefully
1234 write(*,*) 'Data directory, '//trim(dir)//' does not exist or could not write there!'
STOP
end subroutine
Note that this is not foolproof, as it is assumed "dir" has the trailing "/" or "\" depending on the OS being used.

Related

How to handle an optional group in a Fortran Namelist

I am working with a code originally written in Fortran 77 that makes use of namelists (supported by compiler extension at the time of its writing - this feature only became standard in Fortran 90) for reading input files. The namelist input files have groups of namelist variables in between (multiple) plain text headers and footers (see example.nml). Some groups of namelist variables are only read if certain conditions are met for previously read variables.
When reading all the namelist groups in a file in sequence, executables compiled with gfortran, ifort and nagfor all behave the same and give the expected output. However, when a given namelist group in the input file is to be skipped (the optional reading), gfortran and ifort executables handle this as desired, while the executable compiled with nagfor raises a runtime error:
Runtime Error: reader.f90, line 27: Expected NAMELIST group /GRP3/ but found /GRP2/
Program terminated by I/O error on unit 15 (File="example.nml",Formatted,Sequential)
As a minimal working example reproducing the problem, consider the namelist file example.nml and driver program reader.f90 given below, in which NUM2 from namelist group GRP2 should only be read if NUM1 from namelist group GRP1 equals 1:
example.nml:
this is a header
&GRP1 NUM1=1 /
&GRP2 NUM2=2 /
&GRP3 NUM3=3 /
this is a footer
reader.f90:
program reader
implicit none
character(len=40) :: hdr, ftr
integer :: num1, num2, num3, icode
! namelist definition
namelist/grp1/num1
namelist/grp2/num2
namelist/grp3/num3
! open input file
open(unit=15, file='example.nml', form='formatted', status='old', iostat=icode)
! read input data from namelists
read(15, '(a)') hdr
print *, hdr
read(15, grp1)
print *, num1
if (num1 == 1) then
read(15, grp2)
print *, num2
end if
read(15,grp3)
print *, num3
read(15, '(a)') ftr
print *, ftr
! close input file
close(unit=15)
end program reader
All executables give this expected output when NUM1=1:
this is a header
1
2
3
this is a footer
However, when e.g. NUM1=0, the executables compiled with gfortran and ifort give the desired output:
this is a header
0
3
this is a footer
while the executable compiled with nagfor (which is known for being strictly standard conforming), reads the header and first namelist group:
this is a header
0
but then terminates with the previously mentioned runtime error.
As indicated by the error message, example.nml is accessed sequentially, and if that is the case, /GRP2/ is the next record to be read, not /GRP3/ as the program logic asks for, so the error message makes sense to me.
So my question is this:
Can the shown behaviour be attributed to standard (non-)conformance enforced by nagfor and not gfortran and ifort?
If so, does this mean that the non-sequential reading observed with gfortran and ifort is due to extensions supported by these compilers (and not nagfor)? Can this be turned on/off using compiler flags?
The simplest workaround I can think of (minimal change to a large existing program), would be to add a dummy read(15,*) in an else branch for the if statement in reader.f90. This seems to work with all the mentioned compilers. Would this make the code standard conforming (Fortran 90 or later)?
These are the compiler versions and options that were used to compile the executables:
GNU Fortran (Ubuntu 9.1.0-2ubuntu2~18.04) 9.1.0:
gfortran -Wall -Wextra -fcheck=all -g -Og -fbacktrace reader.f90
Intel(R) Visual Fortran, Version 16.0 Build 20160415:
ifort -Od -debug:all -check:all -traceback reader.f90
NAG Fortran Compiler Release 6.1(Tozai) Build 6116:
nagfor -O0 -g -C reader.f90
When namelist formatting is requested on an external file, the namelist record is taken to commence at the record at the current position of the file.
The structure of a namelist input record is well defined by the language specification (see Fortran 2018 13.11.3.1, for example). In particular, this does not allow a mismatching namelist group name. nagfor complaining about this does so legitimately.
Several compilers do indeed appear to continue skipping over records until the namelist group is identified in a record, but I'm not aware of compiler flags available to control that behaviour. Historically, the case was generally that multiple namelists would be specified using distinct files.
Coming to your "simple workaround": this is, alas, not sufficient in the general case. Namelist input may consume several records of an external file. read(15,*) will advance the file position by only a single record. You will want to advance to after the terminating record for the namelist.
When you know the namelist is just that single record then the workaround is good.
#francescalus' answer and comment on that answer, clearly explained the first two parts of my question, while pointing out a flaw in the third part. In the hope that it may be useful to others that stumble upon a similar problem with a legacy code, here is the workaround I ended up implementing:
In essence, the solution is to ensure that the file record marker is always positioned correctly before attempting any namelist group read. This positioning is done in a subroutine that rewinds an input file, reads through records until it finds one with a matching group name (if not found, an error/warning can be raised) and then rewinds and repositions the file record marker to be ready for a namelist read.
subroutine position_at_nml_group(iunit, nml_group, status)
integer, intent(in) :: iunit
character(len=*), intent(in) :: nml_group
integer, intent(out) :: status
character(len=40) :: file_str
character(len=:), allocatable :: test_str
integer :: i, n
! rewind file
rewind(iunit)
! define test string, i.e. namelist group we're looking for
test_str = '&' // trim(adjustl(nml_group))
! search for the record containing the namelist group we're looking for
n = 0
do
read(iunit, '(a)', iostat=status) file_str
if (status /= 0) then
exit ! e.g. end of file
else
if (index(adjustl(file_str), test_str) == 1) then
! backspace(iunit) ?
exit ! i.e. found record we're looking for
end if
end if
n = n + 1 ! increment record counter
end do
! can possibly replace this section with "backspace(iunit)" after a
! successful string compare, but not sure that's legal for namelist records
! thus, the following:
if (status == 0) then
rewind(iunit)
do i = 1, n
read(iunit, '(a)')
end do
end if
end subroutine position_at_nml_group
Now, before reading any (possibly optional) namelist group, the file is positioned correctly first:
program new_reader
implicit none
character(len=40) :: line
integer :: num1, num2, num3, icode
! namelist definitions
namelist/grp1/num1
namelist/grp2/num2
namelist/grp3/num3
! open input file
open(unit=15, file='example.nml', access='sequential', &
form='formatted', status='old', iostat=icode)
read(15, '(a)') line
print *, line
call position_at_nml_group(15, 'GRP1', icode)
if (icode == 0) then
read(15, grp1)
print *, num1
end if
if (num1 == 1) then
call position_at_nml_group(15, 'GRP2', icode)
if (icode == 0) then
read(15, grp2)
print *, num2
end if
end if
call position_at_nml_group(15, 'GRP3', icode)
if (icode == 0) then
read(15, grp3)
print *, num3
end if
read(15, '(a)') line
print *, line
! close input file
close(unit=15)
contains
include 'position_at_nml_group.f90'
end program new_reader
Using this approach eliminates uncertainty in how different compilers treat not finding matching namelist groups at the current record in a file, generating the desired output for all compilers tested (nagfor, gfortran, ifort).
Note: For brevity, only a bare minimum of error checking is done in the code snippet shown here, this (and case insensitive string comparison!) should probably be added.

Fortran inquire return internal file

I have a situation unexpected with my fortran program.
I want to drop some data in a text file. For some reasons, I created a type to deal with this file.
Therefore, I open a file with the commands (where f is my file type object):
open(newunit = f%unit, &
file = trim(adjustl(f%name)), &
form = 'FORMATTED', &
access = 'STREAM', &
action = 'WRITE', &
status = 'REPLACE', &
iostat = ios)
if(ios /= 0) print '("Problem creating file")', trim(f%name)
Then I write stuff like this:
write(unit=f%unit,fmt='(100A)') "#Header"
And when it is done, I want to close the file. To do this, I call the subroutine:
subroutine close_file_ascii(f)
implicit none
class(my_file_type) intent(in) :: f
logical :: file_opened, file_exist
integer :: ios
inquire(unit=f%unit, exist=file_exist, opened=file_opened) !, iostat=ios)
print *,'file_exist:', file_exist, 'file_opened:', file_opened, 'ios:', ios
if ((file_opened) .and. (file_exist)) then
close(unit=f%unit)
else
print *,"[WARNING]"
end if
end subroutine close_file_ascii
My problem is in this last subroutine. When I run the program on windows, I get the following error:
Fortran runtime error: Inquire statement identifies an internal file
Error termination. Backtrace
Therefore, I tried to create MWE to understand the problem, but all of them where working well. So couldn't really isolate the problem. Also a strange thing is that when I compile and execute with gfortran on my linux there is no problem, but when I do so on my windows I get the previous error. ( I compile on windows with gfortran version 7.3.0 x86_64-posix-sjlj-rev0, Built by MinGW-W64 )
I already work-around this problem by uncommenting the end of inquire line in the close subroutine, and everything seems to work fine. And I get the following print:
file_exist: T file_opened: T ios: 5018
But I would to understand what is going on. So what could create such internal file error (while the file should not be internal but external)? Is my workaround could be harmful ? Is it a bug ? Is there a better way to close safely an opened file? Any ideas to isolate the problem ?
EDIT
From roygvib's comment, the following test seems to replicate the problem:
program bug
implicit none
integer :: i
character(len=1) :: s
write (s,'(i1)') 0
open(newUnit=i,file='bug.txt',status='unknown')
inquire(unit=i)
end program bug
The update to gfortran 8.1 solved the problem.

fortran overloading interface error

I edit three files
first:add.f90
module MA
implicit none
contains
subroutine show_int(n)
implicit none
integer , intent(in) ::n
write(*,"('n=',I3)") n
return
end subroutine show_int
subroutine show_character(str)
implicit none
character(len=*) ,intent(in) :: str
write(*,"('str=',A)") str
return
end subroutine show_character
end module
second: add.h
interface show
module procedure show_int, show_character
end interface
third:main.f90
program main
use MA
implicit none
include 'add.h'
call show_int(1)
call show(1)
call show_character("Fortran 95")
call show("Fortran 95")
print * ,"hello "
end program
I compile ,gfortran add.f90 main.f90 -o main
I got these errors
add.h:2.2:
包含于 main.f90:4:
module procedure show_int, show_character
1
错误: (1) 语句无法归类
main.f90:6.13:
call show(1)
1
错误: 泛型‘show’在(1)处没有特定的子进程
main.f90:8.24:
call show("Fortran 95") 1
错误: 泛型‘show’在(1)处没有特定的子进程
I don't know why ?
can you help me ?
Thanks
I cannot read the error messages, but:
The module procedure statement can be used only inside the module containing the procedure. Forget the .h file and place the generic interface block into the module. In Fortran 2003 you could use just procedure (without module) and it should work.

How to execute a command on the first and only first time entering fortran subroutine?

I wrote a subroutine for a Fortran program and I want to execute a command (delete file in the program directory and open a new one) the first, and only first time, the subroutine is called. I know that I can achieve this by creating some flag outside of the subroutine, in the main program, that I set to false at program startup and then set to true upon entering the subroutine. Then I could use this flag in an if statement to figure if the commands I want to execute on the initial call should be executed or not. But this requires me modifying the existing program and I didn't want to do that if I could avoid it. Is there some other way to do what I want to do?
An example might be:
subroutine test(a)
implicit none
integer, intent(inout) :: a
logical, save :: first_time=.true.
if(first_time) then
first_time=.false.
a = a + 12345
else
a = a - 67890
end if
end subroutine test
How about using some characteristic of the output file to determine whether or not to delete it? Time stamp, file lock, a particular file extension, etc.

How to copy a file in Fortran 90?

How can I copy a file in fortran 90 in a portable, cross plaform way ?
Use the SYSTEM with your OS's copy command. Practically all compilers support this feature.
You can read/write the file through a stream in Fortran 2003, but in Fortran 90/95 I think this would work to copy an arbitrary file (extremely inefficient though!!)
OPEN(UNIT=ISRC, FILE='', ACCESS='DIRECT', STATUS='OLD', ACTION='READ', IOSTAT=IERR, RECL=1)
OPEN(UNIT=IDST, FILE='', ACCESS='DIRECT', STATUS='REPLACE', ACTION='WRITE', IOSTATE=IERR, RE)
IREC = 1
DO
READ(UNIT=ISRC, REC=IREC, IOSTAT=IERR) CHAR
IF (IERR.NE.0) EXIT
WRITE(UNIT=IDST, REC=I) CHAR
IREC = IREC + 1
END DO
Of course, if it was a fortran generated file, you could use that information to make it more efficient.
On a personal note: if you need invoke system calls from inside fortran, what are you doing? Isn't it better to use some other language that is better suited for the task?
Yes, Fortran has pathetic I/O and shouldn't be used for this sort of thing if at all possible. What a shame that some of us are forced to do it.
I just read the source file and simultaneously write to the destination, line-by-line. So far this works for me, but is very inefficient.
Dealing with files and portability is annoying with Fortran, and SYSTEM calls are often not very good either. The windows OS doesn't properly follow linux linked files, and Windows/Linux/MacOS have different separaters, I have been caught out with stack limits inherent in the SYSTEM call, and so on.
Good luck !
For Intel Fortran
subroutine copy_file (file_name, file_name_new)
! copies a file file_name to file_name_new
! file_name and file_name_new must include the path information and may include wildcard characters
USE ifport
implicit character*100 (f)
character*1000 fnam
logical*4 logical_result
len1 = len_trim(file_name); len2 = len_trim(file_name_new)
fnam = 'copy/y ' //file_name(1:len1) //' '//file_name_new(1:len2)
l = len_trim(fnam)
logical_result = systemqq(fnam(1:l))
return
end
The previous answer didn't work for me so I wrote the following subroutine
!=============================================================================================================================!
! !
! This subroutine copies file_name to file_name_new writing command to cmd !
! !
!=============================================================================================================================!
subroutine copy_file (file_name, file_name_new)
use ifport
implicit none
!=============================================================================================================================
! D e c l a r a t i o n s
!=============================================================================================================================
character(len=*),intent(IN) :: file_name_new,file_name
!-----------------------------------------------------------------------------------------------------------------------------
logical :: logical_result
!=============================================================================================================================
! S t a t e m e n t s
!=============================================================================================================================
logical_result = systemqq('copy "'//trim(file_name) //'" "'//trim(file_name_new)//'"')
!==============================================================================================================================
end subroutine copy_file
! For Compaq/Intel Visual Fortran
subroutine copy_file(source_,dest_)
use kernel32,only:CopyFile,FALSE
implicit none
integer ret
character*(*), intent(in) :: source_, dest_
ret = CopyFile(trim(source_)//""C, trim(dest_)//""C, FALSE)
end subroutine copy_file