0

How do I invoke a program and pass it standard input? Hypothetical example in Bash:

(echo abc; echo abba) | tr b B

Note that:

  • I don't have the input in a string (I'm generating it as I iterate)
  • I don't know how long input is
  • The input may span multiple lines, as in this example

I've written this in 19 other languages already, the way I usually approach it is to get a file descriptor for the program's standard input, and then write to the file descriptor the same way I would write to standard output.

What I've tried so far: Based on Invoke external program and pass arguments I tried passing it to echo and using the shell to handle the piping. This doesn't work if my input has single quotes in it, and it doesn't work if I don't have my input in a string (which I don't)

Here is my code, currently trying to pull it off by calculating the string that will be printed (it fails right now).

Community
  • 1
  • 1
Joshua Cheek
  • 30,436
  • 16
  • 74
  • 83
  • please create a minimal example and include it in the question (hint if its more the 3 or 4 lines its too much for this simple issue,) Why on earth are you copying strings one character at a time?? I cant tell if your problem lies with execute_command_line, or your convoluted string handling. – agentp Oct 02 '16 at 13:22
  • Your code shoud be **within the question**, not at some external link. It should be short enough. You have very high reputation to know that your github repo can be deleted making the question incomplete. – Vladimir F Героям слава Oct 02 '16 at 13:47
  • BTW if your code is full of nonstadard extensions like FGET, IARGC, GETCWD, you are doing something wrong. The suffix .f95 also isn't a very good idea (use .f90). – Vladimir F Героям слава Oct 02 '16 at 13:53
  • FFS, there is a minimal example in the question: `(echo abc; echo abba) | tr b B` How do you do that in Fortran? – Joshua Cheek Oct 02 '16 at 20:08
  • @VladimirF Why is f90 better than f95? I assume these numbers are years, so f95 would have 5 years of features that f90 doesn't. Eg when I was using f77, I had to pointlessly put 7 spaces in front of every line. Maybe they figured out how to invoke an external program between 1990 and 1995. – Joshua Cheek Oct 02 '16 at 20:11
  • http://stackoverflow.com/questions/20269076/correct-suffix-for-fortran-2003-source-file-intel-fortran-compiler – Vladimir F Героям слава Oct 02 '16 at 20:12
  • @VladimirF What is the standard way to access argv and cwd? – Joshua Cheek Oct 02 '16 at 20:14
  • get_command, get_command_argument, command_argument_count, for cwd I would use get_environment_variable for $PWD – Vladimir F Героям слава Oct 02 '16 at 20:17
  • @VladimirF I updated the arg accessing. The env var doesn't work b/c that gets set by the shell, not the OS, so when my test program cd's to the test dir and then invokes it, there is nothing updating the environment variable. It is better to use `getcwd`, as that surely delegates to `getcwd(3)` in C's stdlib, which asks the OS. `fget` is obnoxious but Fortran can't read in an entire line from stdin since we don't know how long the line is. Of course, the thing preventing my program from working is what this Q is about: it seems Fortran can't invoke an external program and print to its stdin. – Joshua Cheek Oct 02 '16 at 21:21
  • You should not normally need fget. It is quite possible that what you are trying to do is not covered by the standard, why should it be? But I am still not sure I fully understand what you are trying to do. If it is a POSIX call, just call that C function. Fortran is not for systems programming. – Vladimir F Героям слава Oct 02 '16 at 21:26
  • I don't even get whether your program calls the external program or receives the data. It is all very unclear. Maybe if I knew more bash, but I don't... – Vladimir F Героям слава Oct 02 '16 at 21:33
  • As for pipe, there seem to be several similar questions: http://stackoverflow.com/questions/37763832/fortran-pipe-to-program/37776740#37776740 – roygvib Oct 02 '16 at 22:34
  • 1
    `f90` is defacto standard extension for freeform file format fortran. (ie f90 and up) you do not need to carry along with different extensions indicating specific versions. – agentp Oct 03 '16 at 11:45

2 Answers2

0

As for single quotation, how about inserting the escape character (\) before each quotation mark (')...? It seems to be working somehow for the "minimal" example:

module utils
    implicit none
contains

function esc( inp ) result( ret )
    character(*), intent(in) :: inp
    character(:), allocatable :: ret
    integer :: i

    ret = ""
    do i = 1, len_trim( inp )
        if ( inp( i:i ) == "'" ) then
            ret = ret // "\'"
        else
            ret = ret // inp( i:i )
        endif
    enddo
endfunction

endmodule

program test
    use utils
    implicit none
    character(100) :: str1, str2
    integer i

    call getcwd( str1 )   !! to test a directory name containing single quotes
    str2 = "ab'ba"

    print *, "trim(str1) = ", trim( str1 )
    print *, "trim(str2) = ", trim( str2 )
    print *
    print *, "esc(str1) = ", esc( str1 )
    print *, "esc(str2) = ", esc( str2 )

    print *
    print *, "using raw str1:"
    call system( "echo " // trim(str1) // " | tr b B" )

    print *
    print *, "using raw str2:"
    call system( "echo " // trim(str2) // " | tr b B" )  !! error

    print *
    print *, "using esc( str1 ):"
    call system( "echo " // esc(str1)  // " | tr b B" )

    print *
    print *, "using esc( str2 ):"
    call system( "echo " // esc(str2)  // " | tr b B" )

    print *
    print *, "using esc( str1 ) and esc( str2 ):"
    call system( "(echo " // esc(str1) // "; echo " // esc(str2) // ") | tr b B" )

    ! pbcopy (copy to pasteboard; mac-only)
    ! call system( "(echo " // esc(str1) // "; echo " // esc(str2) // ") | pbcopy" )

endprogram

If we run the above program in the directory /foo/baa/x\'b\'z, we obtain

 trim(str1) = /foo/baa/x'b'y
 trim(str2) = ab'ba

 esc(str1) = /foo/baa/x\'b\'y
 esc(str2) = ab\'ba

 using raw str1:
/foo/baa/xBy

 using raw str2:
sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file

 using esc( str1 ):
/foo/baa/x'B'y

 using esc( str2 ):
aB'Ba

 using esc( str1 ) and esc( str2 ):
/foo/baa/x'B'y
aB'Ba
roygvib
  • 7,218
  • 2
  • 19
  • 36
0

This is what I mean by a minimal example.

 call execute_command_line("(echo ""hexxo world"";echo abba)|tr x l")
 end
  hello world
  abba

Is this not doing exactly what you ask, invoking tr and passing standard input?

agentp
  • 6,849
  • 2
  • 19
  • 37
  • Well, the issue I have is that I don't have the input as a String. I can possibly build it, but I don't know how long it is, which seems to be an issue for Fortran (ie I cannot allocate the string to save into since I don't know its length). I figured it would be easier to periodically print to a stream than to have two versions of my algorithm, one which calculates the size and the other which copies it into the string I am ultimately going to print. Also, there can be any kind of content in the string, including newlines and quotes, which may mess up the shell string. – Joshua Cheek Oct 09 '16 at 07:54
  • if you need help constructing a string that might be a new question, but typically you use the `char` function for newlines and such. fortran2003+ has an intrinsic `new_line` as well. – agentp Oct 09 '16 at 13:11