I have a custom .exe
program that performs computations. It can be run manually from Windows Command Line, but I created a .bat
file for efficiency. The .bat
file launches the .exe
in Windows Command Line and pipes in the inputs. Specifically, it reads a list of 3 files from runs.txt
and passes each file in the list to the program. The program then reads the info from the file, performs the computations, and saves the outputs.
I am trying to convert this workflow into bash so that I can run it with other workflows using Git Bash. I don't know how to convert the piping syntax to bash.
Folder structure:
myfolder
|---- myprogram.exe
|---- myshell.sh
|---- mybatch.bat
|---- runs.txt
|---- RunA.csv
|---- RunB.csv
|---- RunC.csv
Contents of runs.txt
:
RunA.csv
RunB.csv
RunC.csv
Here is the contents of mybatch.bat
which works correctly when I run from Windows Command Line:
(for %%i in (3 runs.txt) do @echo %%i)| myprogram.exe
When I am in Git Bash terminal with myfolder
as the pwd
, I can run the following and it works from terminal:
cmd
(for %i in (3 runs.txt) do @echo %i)| myprogram.exe
exit
(Note that use %i when running from terminal and %%i when running from batch.) I know it is working because it prints the expected outputs and creates the expected files.
However, when I try to put this into myshell.sh
and run sh myshell.sh
from Git Bash terminal, nothing happens. No outputs are printed and no files are created. The terminal does, however, switch to Windows Command Line, so I think it is just reading the first line (cmd
) and stopping.
Contents of myshell.sh
:
cmd
(for %%i in (3 runs.txt) do @echo %%i)| myprogram.exe
(Note I also tried with just %i; same problem)
How can I get it to proceed to the next line as if I was entering the command by hand in terminal? Looking for solutions that will work in Git Bash; not using other bash emulators.
Update
The code (for %%i in (3 runs.txt) do @echo %%i)| myprogram.exe
needs to work in bash. It can do that by using CMD or running the EXE in bash. But it must read the input file name from the text file and pass it into the EXE.
Correction. Your mybatch.bat
does NOT "pass[] each file in [runs.txt]" to the program. It passes only the count 3 and the filename runs.txt. If something is reading 3 filenames from runs.txt and processing them, it is the program, not CMD. Aside: when running programs in CMD you can always omit .exe
(or .com
or several other suffixes) from the name if you want.
Why it doesn't work. When you are running git-bash and you type
cmd
(for %i in (3 runs.txt) do @echo %i)| myprogram.exe
exit
only the first line cmd
is read by git-bash, causing it to run cmd.exe
(which is actually CMD.EXE
in Windows case-insensitive filesystem) with input from and output to the terminal; then the second and third lines are read and executed by CMD.EXE. This works because they use syntax valid for CMD.EXE, even though it is not valid for git-bash.
However if you put those same three lines in a file and try to execute it as a script in git-bash, git-bash executes the first line by running CMD.EXE
with input from and output to the terminal. That CMD doesn't receive, and cannot execute, the second and third lines.
The minimal fix. If you want to use CMD to execute those lines (as is necessary with that syntax) you should provide them as input to CMD. The simplest way is to use a 'here-document' by making your script file contain:
cmd <<ENDINPUT
(for %i in (3 runs.txt) do @echo %i)| myprogram.exe
exit
ENDINPUT
ENDINPUT can actually be any 'word' (string of non-special characters) you like as long as both instances (the << on the first line and the last line) are the same; many people like to use EOF.
But you don't actually need the exit
command since end-of-input does the same thing in CMD, so you can do this:
cmd <<ENDINPUT
(for %i in (3 runs.txt) do @echo %i)| myprogram.exe
ENDINPUT
Better options. You don't actually need a for
loop at all; in CMD (either interactively or in a batch file) you could simply do
( echo 3 & echo runs.txt ) | myprogram.exe
and in git-bash (or any other shell that runs on Windows, like cygwin), rather that going roundabout through CMD, you could directly run the program with the same input, with
printf '%s\r\n' 3 runs.txt | myprogram.exe
# in git-bash you can omit the .exe if 'myprogram' is unambiguous
or if myprogram.exe
reads its input so that the Windows convention of CRLF is not required and lone-LF works (which is true of all C and C++ libraries on Windows I know of, for example) then you can use simpler
{ echo 3; echo runs.txt; } | myprogram.exe
or even (in bash but not all other shells)
myprogram.exe <<<$'3\nruns.txt'