1

How do I invoke a batch file from a bash script under Cygwin, with...

  • absolute path to the batch file
  • batch file argument(s) representing absolute (windows) path(s)
  • output redirection into a file at an absolute path

where...

  • all paths may contain whitespace and special characters like parentheses
  • all paths are initially present only in Unix format within the bash script?

I have a windows batch script C:\Program Files (x86)\bin\script.bat, which takes a (windows) path to an input file as argument - example:

@ECHO OFF
echo "This is script.bat. The value of the first argument is [%~1]"

I want to redirect the output of script.bat into an output file at another absolute path.

On the windows command prompt (cmd.exe), I would invoke the command like this:

C:\> "C:\Program Files (x86)\bin\script.bat" "C:\Input Path\input.txt" > "C:\Output Path\output.txt"
output.txt: This is script.bat. The value of the first argument is [C:\Input Path\input.txt].

I can omit the double quotes around the script and the redirection target if I apply "batch-style escaping", but that doesn't work for the argument (for some reason):

C:\> C:\Program^ Files^ ^(x86^)\bin\script.bat "C:\Input Path\input.txt" > C:\Output^ Path\output.txt
output.txt: This is script.bat. The value of the first argument is [C:\Input Path\input.txt].

...but:

C:\> C:\Program^ Files^ ^(x86^)\bin\script.bat C:\Input^ Path\input.txt > C:\Output^ Path\output.txt
output.txt: This is script.bat. The value of the first argument is [C:\Input].

I can also wrap the command into an additional call to cmd.exe, using additional double quotes around the whole term:

C:\> cmd.exe /C ""C:\Program Files (x86)\bin\script.bat" "C:\Input Path\input.txt" > "C:\Output Path\output.txt""
output.txt: This is script.bat. The value of the first argument is [C:\Input Path\input.txt].

C:\> cmd.exe /C "C:\Program^ Files^ ^(x86^)\bin\script.bat "C:\Input Path\input.txt" > C:\Output^ Path\output.txt"
output.txt: This is script.bat. The value of the first argument is [C:\Input Path\input.txt].

I can also apply the output redirection to the outer cmd.exe instance:

C:\> cmd.exe /C ""C:\Program Files (x86)\bin\script.bat" "C:\Input Path\input.txt"" > "C:\Output Path\output.txt"
output.txt: This is script.bat. The value of the first argument is [C:\Input Path\input.txt].

C:\> cmd.exe /C "C:\Program^ Files^ ^(x86^)\bin\script.bat "C:\Input Path\input.txt"" > C:\Output^ Path\output.txt
output.txt: This is script.bat. The value of the first argument is [C:\Input Path\input.txt].

So far so good. But how can I invoke any of the above command lines from a bash script under Cygwin?

All paths initially exist only in Unix format:

#!/bin/sh
BIN_UNIX="/cygdrive/c/Program Files (x86)/bin/script.bat"
ARG_UNIX="/cygdrive/c/Input Path/input.txt"
OUT_UNIX="/cygdrive/c/Output Path/output.txt"

Note that the file $OUT_UNIX does not exist at the time the script is called (so 'cygpath --dos ...' doesn't work).

I have experimented with dozens of more or less clumsy combinations of Unix / Windows paths (converting with cygpath), no quotes, single quotes, double quotes, no escaping, "bash-style" escaping, "batch-style" escaping, etc. The only working solution I could find depends on an 8.3-style path for the batch script, which eliminates white spaces and special characters:

#!/bin/sh
# [...]
BIN_DOS=$( cygpath --dos "${BIN_UNIX}" )
ARG_WIN=$( cygpath --windows "${ARG_UNIX}" )
cmd /C "${BIN_DOS}" "${ARG_WIN}" > "$OUT_UNIX"

But there must be a more systematic and robust way to do this, right?


Related questions which all don't fully answer my question:

Why is it that Cygwin can run .bat scripts?

Windows batches in Cygwin with spaces in path and arguments

correct quoting for cmd.exe for multiple arguments

How to Pass Command Line Parameters with space in Batch File

2
  • Where is the Batch file question in this. You want NON WINDOWS answers. Commented Apr 29, 2019 at 8:32
  • @Noodles The tricky point is the transition from bash to batch, the quoting/escaping can be really exciting. I spent many hours to examine similar problems Commented Apr 29, 2019 at 12:36

1 Answer 1

3

You could use

BIN_WIN=$( cygpath --windows "${BIN_UNIX}" )
ARG_WIN=$( cygpath --windows "${ARG_UNIX}" )
OUT_WIN=$( cygpath --windows "${OUT_UNIX}" )
cmd /c CALL "$BIN_WIN" "${ARG_WIN}" > "$OUT_WIN"

The important point is the CALL in front of your command.

cmd /c fails to start a command with spaces inside, because it splits the command even when there are quotes around.

The cmd /c would also work without CALL, but with additional enclosing quotes.

cmd /c "  "C:\Program Files (x86)\bin\script.bat" "args"   "

But it seems to be impossible to transfer raw quotes from cygwin to cmd.exe, because cygwin translates quotes to \", so the expression becomes

cmd /c \"  "C:\Program Files (x86)\bin\script.bat" "args"   \"
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.