How to invoke a program and pass it standard input - fortran

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).

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

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?

Related

Why does the output appear black?

Operating System : Windows 10
Compiler : XStart
Command when running the program : pgf90 prgramname.f90
running program command : a.out
Program code I wrote :
Program silta
implicit none
CHARACTER :: Str_1 = " For", Str_2 =" tran", z
z = Str_1 // Str_2 // " -90"
print *, z
end program silta
When I run this program, there is no result and only black space.
Probably the result of the problem is the Fortran-90, but how do I get it to print properly?
This line
CHARACTER :: Str_1 = " For", Str_2 =" tran", z
declares 3 character variables, each one character long. What you get, therefore, is str1 == " ", str2 == " " and z unset. Then this line
z = Str_1 // Str_2 // " -90"
is interpreted as
z = " " // " " // " -90"
but only the first character, which is a space, gets into z. And that's what you see when you print it out, or rather, that's what you don't see, it's just a space.
Go back to your Fortran tutorial and learn about character lengths, perhaps start with
CHARACTER(len=8) :: Str_1 = " For", Str_2 =" tran", z
As VladimirF has commented, learn also the intrinsic function trim for getting rid of trailing spaces in character variables. There are other useful intrinsic string functions too, worthy of your research.

Parsing Challenge - Fixing Broken Syntax

I have thousands of lines of code making use of a particular non-standard syntax. I need to be able to compile the code with a different compiler, that does not support this syntax. I have tried to automatize the changes that need to be made, but being not very good with regex etc. I have failed.
Here is what I want to achieve: currently in my code an object's methods and variables are called/accessed with the following possible syntaxes:
call obj.method()
obj.method( )
obj.method( arg1, arg2, kwarg1=kwarg1 )
obj1.var = obj2.var2
Instead I want this to be:
call obj%method()
obj%method( )
obj%method( arg1, arg2, kwarg1=kwarg1 )
obj1%var = obj2%var2
And I want to make these changes without effecting the following possible occurrences of "."s:
Decimal numbers:
a = 1.0
b = 1.d0
Logical opertors (note possible spaces and method calls):
if (a.or.b) then
if ( a .and. .not.(obj.l1(1.d0)) ) then
Anything that is commented (the exclamation point "!" is used for this purpose)
!>I am a commented line.
! > I am.a commented line with..leading blanks and extra periods.1.
b=a1.var( 0.d0 ) !! I contain a commented version of this line: b=a1.var( 0.d0 )
Anything that is in quotes (i.e. string literals)
c = "I am a string"
c= 'I am an obnoxious string: b=a1.var( 0.d0 ) ... '
Does anyone know how to approach this. I guess regex is the natural approach, but I am open to anything. (In case anyone cares: the code is written in fortran. ifort is happy with the "." syntax; gfortran isn't)
Have you looked into solving the problem with flex? It uses regular expressions, but is more advanced, as it tries different patterns and returns the longest matching option. The rules could look like this:
%% /* rule part of the program */
!.*\n printf(yytext); /* ignore comments */
\".*\"|'.*' printf(yytext); /* ignore strings */
[^A-Za-z_][0-9]+\. printf(yytext); /* ignore numbers */
".and."|".or."|".not." printf(yytext); /* ignore logical operators */
\. printf("%%"); /* now, replace the . by % */
[^\.] printf(yytext); /* ignore everything else */
%% /* invoke the program */
int main() {
yylex();
}
You may have to modify the third line. Currently it ignores any . that occurs after any number of digits, if there is none of the characters from A to Z, from a to z or the character _ before the digits. If there are more legal characters in identifiers, you can add them.
If everything is correct, you should be able to turn that into a program. Copy it into a file called lex.l and execute:
$ flex -o lex.yy.c lex.l
$ gcc -o lex.out lex.yy.c -lfl
Then you have the C program lex.out. You can just use that in the command line:
cat unreplaced.txt | ./lex.out > replaced.txt
This uses the same principle as Ed Mortons suggestion, but it uses flex so we can skip the organization. It still fails in some cases like having \" inside strings.
Sample input
call obj.method()
obj.method( )
obj.method( arg1, arg2, kwarg1=kwarg1 )
obj1.var = obj2.var2
a = 1.0
b = 1.d0
if (a.or.b) then
if ( a .and. .not.(obj.l1(1.d0)) ) then
!>I am a commented line.
! > I am.a commented line with..leading blanks and extra periods.1.
b=a1.var( 0.d0 ) !! I contain a commented version of this line: b=a1.var( 0.d0 )
c = "I am a string"
c= 'I am an obnoxious string: b=a1.var( 0.d0 ) ... '
c="I am an exclaimed string!"; b=a1.var()
Output
call obj%method()
obj%method( )
obj%method( arg1, arg2, kwarg1=kwarg1 )
obj1%var = obj2%var2
a = 1.0
b = 1.d0
if (a.or.b) then
if ( a .and. .not.(obj%l1(1.d0)) ) then
!>I am a commented line.
! > I am.a commented line with..leading blanks and extra periods.1.
b=a1%var( 0.d0 ) !! I contain a commented version of this line: b=a1.var( 0.d0 )
c = "I am a string"
c= 'I am an obnoxious string: b=a1.var( 0.d0 ) ... '
c="I am an exclaimed string!"; b=a1%var()
You can't do this 100% robustly without a language parser (e.g. the following will fail in some cases if you have \" inside double quoted strings - easily handled but just one of many possible failures not covered by your use cases) but this will handle what you've shown us so far and a bit more. It uses GNU awk for gensub() and the 3rd arg to match().
Sample Input:
$ cat file
call obj.method()
obj.method( )
obj.method( arg1, arg2, kwarg1=kwarg1 )
obj1.var = obj2.var2
a = 1.0
b = 1.d0
if (a.or.b) then
if ( a .and. .not.(obj.l1(1.d0)) ) then
!>I am a commented line.
! > I am.a commented line with..leading blanks and extra periods.1.
b=a1.var( 0.d0 ) !! I contain a commented version of this line: b=a1.var( 0.d0 )
c = "I am a string"
c= 'I am an obnoxious string: b=a1.var( 0.d0 ) ... '
c="I am an exclaimed string!"; b=a1.var()
Expected Output:
$ cat out
call obj%method()
obj%method( )
obj%method( arg1, arg2, kwarg1=kwarg1 )
obj1%var = obj2%var2
a = 1.0
b = 1.d0
if (a.or.b) then
if ( a .and. .not.(obj%l1(1.d0)) ) then
!>I am a commented line.
! > I am.a commented line with..leading blanks and extra periods.1.
b=a1%var( 0.d0 ) !! I contain a commented version of this line: b=a1.var( 0.d0 )
c = "I am a string"
c= 'I am an obnoxious string: b=a1.var( 0.d0 ) ... '
c="I am an exclaimed string!"; b=a1%var()
The Script:
$ cat tst.awk
{
# give us the ability to use #<any other char> strings as a
# replacement/placeholder strings that cannot exist in the input.
gsub(/#/,"#=")
# ignore all !s inside double-quoted strings
while ( match($0,/("[^"]*)!([^"]*")/,a) ) {
$0 = substr($0,1,RSTART-1) a[1] "#-" a[2] substr($0,RSTART+RLENGTH)
}
# ignore all !s inside single-quoted strings
while ( match($0,/('[^']*)!([^']*')/,a) ) {
$0 = substr($0,1,RSTART-1) a[1] "#-" a[2] substr($0,RSTART+RLENGTH)
}
# Now we can separate comments from what comes before them
comment = gensub(/[^!]*/,"",1)
$0 = gensub(/!.*/,"",1)
# ignore all .s inside double-quoted strings
while ( match($0,/("[^"]*)\.([^"]*")/,a) ) {
$0 = substr($0,1,RSTART-1) a[1] "##" a[2] substr($0,RSTART+RLENGTH)
}
# ignore all .s inside single-quoted strings
while ( match($0,/('[^']*)\.([^']*')/,a) ) {
$0 = substr($0,1,RSTART-1) a[1] "##" a[2] substr($0,RSTART+RLENGTH)
}
# convert all logical operators like a.or.b to a##or##b so the .s wont get replaced later
while ( match($0,/\.([[:alpha:]]+)\./,a) ) {
$0 = substr($0,1,RSTART-1) "##" a[1] "##" substr($0,RSTART+RLENGTH)
}
# convert all obj.var and similar to obj%var, etc.
while ( match($0,/\<([[:alpha:]]+[[:alnum:]_]*)[.]([[:alpha:]]+[[:alnum:]_]*)\>/,a) ) {
$0 = substr($0,1,RSTART-1) a[1] "%" a[2] substr($0,RSTART+RLENGTH)
}
# Convert all ##s in the precomment text back to .s
gsub(/##/,".")
# Add the comment back
$0 = $0 comment
# Convert all #-s back to !s
gsub(/#-/,"!")
# Convert all #=s back to #s
gsub(/#=/,"#")
print
}
Running The Script And Its Output:
$ awk -f tst.awk file
call obj%method()
obj%method( )
obj%method( arg1, arg2, kwarg1=kwarg1 )
obj1%var = obj2%var2
a = 1.0
b = 1.d0
if (a.or.b) then
if ( a .and. .not.(obj%l1(1.d0)) ) then
!>I am a commented line.
! > I am.a commented line with..leading blanks and extra periods.1.
b=a1%var( 0.d0 ) !! I contain a commented version of this line: b=a1.var( 0.d0 )
c = "I am a string"
c= 'I am an obnoxious string: b=a1.var( 0.d0 ) ... '
c="I am an exclaimed string!"; b=a1%var()

Spaces in fortran string output

I have the following Fortran 95 code:
program write_test
CHARACTER(LEN=3) :: str
write(*,*) (' ',"""",'File'//trim(str(i)),"""",' ',"""",'Frequency'//trim(str(i)),"""",i=1,5)
end program write_test
!character*(*) function str(k)
character(*) function str(k)
! "Convert an integer to string."
integer, intent(in) :: k
write (str, *) k
str = adjustl(str)
end function str
When I compile and run it, I get the following output:
" File1" " Frequency1" " File2" " Frequency2" " File3" " Frequency3" " File4" " Frequency4" " File5" " Frequency5"
Why is there a space between the double quote and the letter F?
For list-directed output with the * "format", the compiler has freedom to insert one or more spaces between the items printed. For more precise control, use a format string, as in the code below, tested with g95 and gfortran. You can use the i0 format to print an integer without spaces.
program write_test
character(len=3) :: str
write (*,"(100a)") (" ","""","File"//trim(str(i)),"""", &
" ","""","Frequency"//trim(str(i)),"""",i=1,5)
! preferred way is line below
write (*,"(100(1x,2a,i0,a))") ("""","File",i,"""","""","Frequency",i,"""",i=1,5)
end program write_test
character(*) function str(k)
! Convert an integer to string
integer, intent(in) :: k
write (str,"(i0)") k
str = adjustl(str)
end function str

reading delimiter with fortran

I need to read this kind of file. I have problem reading the delimiters within the file.
xxxx
dd/mm/yyyy
text
text
angle
Number of points
-13.82|654781.695|292510.337|4.889|SD
0.00|654795.515|292510.337|4.373|P1
1.25|654796.765|292510.337|4.324|SD
1.29|654796.805|292510.337|4.657|SD
1.68|654797.195|292510.337|4.622|SD
......
(1) Read lines from the file into a string using the "(a)" format.
(2) Replace unwanted delimiters in the string with delimiters recognized by Fortran (spaces or commas).
(3) Get data from the string using an "internal read".
The program below illustrates steps (2) and (3).
program main
implicit none
character (len=20) :: str
integer :: i
real :: x,y
str = "321.1|5678.9"
do i=1,len_trim(str)
if (str(i:i) == "|") str(i:i) = " "
end do
print*,"str = '" // trim(str) // "'" ! output: '321.1 5678.9'
read (str,*) x,y
print*,"x, y =",x,y ! output: x, y = 321.1 5678.9
end program main

What is the difference between these three fscanf calls in OCaml?

I wrote a short bit of code to simply skip num_lines lines in an input file (printing the lines out for debugging purposes. Here's two things I tried that didn't work:
for i = 0 to num_lines do
print_endline (fscanf infile "%s" (fun p -> p));
done;;
for i = 0 to num_lines do
print_endline (fscanf infile "%S\n" (fun p -> p));
done;;
But this one did work:
for i = 0 to num_lines do
print_endline (fscanf infile "%s\n" (fun p -> p));
done;;
I've been trying to comprehend the documentation on fscanf, but it doesn't seem to be sinking in. Could someone explain to me exactly why the last snippet worked, but the first two didn't?
"%s" -- Matches everything to next white-space ("\n" here) but never matches "\n"
"%S\n" -- Matches thing that looks like Ocaml strings, then a "\n"
"%s\n" -- Matches everything to next white-space ("\n" here) then "\n". This will act different if there is no trailing "\n" in file or if there is a space before the "\n", etc.
"%s " -- Matches anything up to white-space, and then all trailing white-space including "\n" (or possibly even no white-space). This works because " " means "any white space, possible none", in the format string.