Silverfrost Forums

Welcome to our forums

Running command line programs

29 Jan 2025 3:46 #31874

Hello,

I have tested the following code (ftn95 v9.06), which works using system but fails with execute_command_line:

program dos_cmd
  integer :: i
  character(len=512) :: cmd

!Input string using programs grdinfo, piping and gawk (programs in PATH)
!Get header info from a netCDF file using GMT
  cmd = 'grdinfo Bathy_crop1_scaled.grd -C | ' // &
        'gawk '{xmin=$2; xmax=$3; ymin=$4; ymax=$5; nx=$10; ny=$11};' // &
         '{print xmin, xmax, ymin, ymax, 2*nx, 2*ny}' > nxy.txt'

  print *, 'Executing command:'
  print *, trim(cmd)

!test alternate command-line calls
  call system(cmd) ! works 
  call execute_command_line(cmd, exitstat=i) ! fails (F2008 intrinsic)

  print *, 'Exit status of external_prog.exe was ', i

end program dos_cmd

system(cmd) correctly writes nxy.txt. However execute_command_line(cmd,exitstat=i) fails with run-time error:

 Executing command:
 grdinfo Bathy_crop1_scaled.grd -C | gawk '{xmin=$2; xmax=$3; ymin=$4; ymax=$5; nx=$10; ny=$11};{print xmin, xmax, ymin,
   ymax, 2*nx, 2*ny}' > nxy.txt
grdinfo [ERROR]: Cannot find file |
grdinfo [ERROR]: Cannot find file gawk
grdinfo [ERROR]: Cannot find file {xmin
 Exit status of external_prog.exe was           72

Any ideas why the second call fails? grdinfo, gawk all sit in the windows PATH.

Not a big issue, just curious to know why there is a difference in behaviour.

Lester

29 Jan 2025 7:20 #31875

What do the optional arguments CMDSTAT, and CMDMSG return?

30 Jan 2025 1:02 #31876

Hi Ken,

Adding the cmdstat and cmdmsg options gives:

 Executing command:
 grdinfo Bathy_crop1_scaled.grd -C | gawk '{xmin=$2; xmax=$3; ymin=$4; ymax=$5; nx=$10; ny=$11};{print xmin, xmax, ymin,
   ymax, 2*nx, 2*ny}' > nxy.txt
grdinfo [ERROR]: Cannot find file |
grdinfo [ERROR]: Cannot find file gawk
grdinfo [ERROR]: Cannot find file {xmin
 Exit status of external_prog.exe was           72
 cmdstat =            0
 cmdmsg =�
�@��@h�`�p���@
��8�8�
  �
�PCNAME
  PCNAME
  @

The extra options, i, j (integers) and csmg (character)

call execute_command_line(cmd, exitstat=i, cmdstat=j, cmdmsg=csmg) ! fails (F2008 intrinsic)
30 Jan 2025 1:13 #31877

A general problem might be the use of the pipe.

Windows does not operate like Linux, specifically that a pipe does not concurrently run at the same time as the 'sending' command.

Or, so I have been told/read.

30 Jan 2025 9:07 #31878

EXECUTE_COMMAND_LINE has an optional logical argument WAIT which when present and set .FALSE. executes the command asynchronously.

But I think that SYSTEM is the same as EXECUTE_COMMAND_LINE with WAIT set .TRUE. which is the default.

30 Jan 2025 12:12 #31880

The solution when using execute_command_line(cmd) is to wrap the command in a shell invoccation, as that was the only way to get it to understand piping and redirection.

  cmd2 = 'cmd.exe /c 'grdinfo Bathy_crop1_scaled.grd -C | ' // &
        'gawk '{xmin=$2; xmax=$3; ymin=$4; ymax=$5; nx=$10; ny=$11};' // &
         '{print xmin, xmax, ymin, ymax, 2*nx, 2*ny}' > nxy_test.txt' '

The output of exitstat and cmdstat are 0 (process succeds), however the output from cmdmsg is garbage

As you say Paul, SYSTEM and EXECUTE_COMMAND_LINE do the same thing.

Lester

30 Jan 2025 1:45 #31881

Lester,

cmdmsg has intent(inout) so needs to be initialised to ' '.

program p
implicit none
integer :: cstat, estat
character(100) :: cmsg
cmsg = ' '

! This will fail since xxx.exe does not exist

call execute_command_line ('xxx.exe', wait=.true., exitstat=estat, cmdstat=cstat, cmdmsg=cmsg)

if (cstat .gt. 0) print *, 'Command execution failed with error ' , trim( cmsg )

end

Returns

 Command execution failed with error Failed to create process.

If an error condition occurs, cmdmsg is assigned a explanatory message. Otherwise, cmdmsg is unchanged.

30 Jan 2025 2:44 #31882

Thanks for the information on cmdmsg Ken, that helps to follow the logic.

30 Jan 2025 3:04 #31883

I always thought that this error is an indication and bad example of users being lazy procrastinators not reporting bugs some of which are deeply hidden and living for centuries not exposed. This bug will outlive us all together. Impression is that the developers also tried to find out why this happening and seems found nothing suspicious.

And the pure EXECUTE_COMMAND_LINE not just does not work sometimes, it can do the whole flurry of unimaginable things. That clearly is one of those proverbial 'devilry' cases cured only with the large amount users and their active reporting.

Meantime as usually we have only workarounds... workarounds. The only way EXECUTE_COMMAND_LINE finally started working amicably in my cases was when Paul ones suggested to use it in a subroutine isolated like this

subroutine LaunchH5dump(iThread)
  call EXECUTE_COMMAND_LINE(text256, exitstat=ierr4)
  if(ierr4.ne.0) then
    call sound@(2000,1) 
    print*,'Sound 2000 error launching h5dump in thread . text256=', trim(text256), iThread
  endif
end subroutine

and then launch it via thread launcher which is calling this subroutine

  ii = START_THREAD@(LaunchH5dump,1)
       call wait_for_thread@ (0)
12 Feb 2025 5:05 #31931

Quick update.

Came across an old bit of code I had and this command works without issue for a long string:

call cissue@('grdinfo xyz-test-gmt.grd -C | ' // &
         'gawk '{xmin=$2; xmax=$3; ymin=$4; ymax=$5; nx=$10; ny=$11};' // &
         '{print xmin, xmax, ymin, ymax, 2*nx, 2*ny}' > nxy.txt', fail)
15 Dec 2025 11:44 #32552

I cannot get this to produce any output:

Program p
   Call Execute_Command_Line ( 'ver > testver.txt', Wait=.true. )
End Program p

not even with an attempt to follow Dan's suggestion:

! Module m
Contains
Subroutine s(ithread)
   Integer(7) :: ithread
   Call Execute_Command_Line ( 'ver > testver3.txt', Wait=.true. )
End Subroutine s
End Module m
Program p
   Use m
   Include <windows.ins>
   Integer(7) :: i,j
   Call System ( 'ver > testver1.txt' )
   Call Execute_Command_Line ( 'ver > testver2.txt', Wait=.true. )
   i = start_thread@( s, j )
   Call wait_for_thread@ ( 0 )
End Program p
16 Dec 2025 3:11 #32553

See if the following program, which is much simpler than what you tried, meets your needs:

Program p
   call system('ver>testver2.txt')
End Program p 
16 Dec 2025 5:34 #32554

Yes, that does work, but my understanding is that System is a FTN95 extension (although I could not find any documentation for it), and I need the code to work across different compilers. Of course, I can, and did, implement a conditional compilation, but if Execute_Command_Line does not work it should at the least return an error, and when using Exitstat I get a return of 0.

16 Dec 2025 8:20 #32555

The following works..

Call Execute_Command_Line ( 'cmd /c ver > testver3.txt', Wait=.true. )

The prefix 'cmd /c' is currently needed for the command 'ver' but not for 'dir'. I have not tested other commands.

In the next release of the library, 'cmd /c' will be prepended if it is not already supplied and this ought to work for all commands.

'Wait=.true.' is the default and can be omitted.

16 Dec 2025 6:40 #32556

Great - that's what I need.

On a related topic, prefixing with cmd /c also gets the following to work:

Call execute_command_line ( 'cmd /c echo %CD%' )

I was trying to get the current directory from the environment variable CD, but the following does not work:

Call get_environment_variable ( 'CD', cdir )

The failure, according to other forums, has little to do with the compiler, and more to do with whether CD is properly defined as an environment variable. Regardless, given that the execute_command_line works, the output can be redirected and then the current directory can then be read easily.

16 Dec 2025 9:49 #32557

Much easier to use is the function CURDIR@().

It returns the full path of the default directory used by the running program at that time. For example, a shortcut whose target directory is set, CURDIR@() will return that folder name if used at the start of the program. That's how I use it.

If you use ATTACH@ later on, then do CURDIR(), it will reflect the new default directory. I use this sequence specifically when a root-relative (.\ or ..\) is input by the user.

Please login to reply.