Is it possible to write a single script file which executes in both Windows (treated as .bat) and Linux (via Bash)?
I know the basic syntax of both, but didn't figure out. It could probably exploit some Bash's obscure syntax or some Windows batch processor glitch.
The command to execute may be just a single line to execute other script.
The motivation is to have just a single application boot command for both Windows and Linux.
Update: The need for system's "native" shell script is that it needs to pick the right interpreter version, conform to certain well-known environment variables etc. Installing additional environments like CygWin is not preferable - I'd like to keep the concept "download & run".
The only other language to consider for Windows is Windows Scripting Host - WSH, which is preset by default since 98.
What I have done is use cmd’s label syntax as comment marker. The label character, a colon (:
), is equivalent to true
in most POSIXish shells. If you immediately follow the label character by another character which can’t be used in a GOTO
, then commenting your cmd
script should not affect your cmd
code.
The hack is to put lines of code after the character sequence “:;
”. If you’re writing mostly one-liner scripts or, as may be the case, can write one line of sh
for many lines of cmd
, the following might be fine. Don’t forget that any use of $?
must be before your next colon :
because :
resets $?
to 0.
:; echo "Hi, I’m ${SHELL}."; exit $?
@ECHO OFF
ECHO I'm %COMSPEC%
A very contrived example of guarding $?
:
:; false; ret=$?
:; [ ${ret} = 0 ] || { echo "Program failed with code ${ret}." >&2; exit 1; }
:; exit
ECHO CMD code.
Another idea for skipping over cmd
code is to use heredocs so that sh
treats the cmd
code as an unused string and cmd
interprets it. In this case, we make sure that our heredoc’s delimiter is both quoted (to stop sh
from doing any sort of interpretation on its contents when running with sh
) and starts with :
so that cmd
skips over it like any other line starting with :
.
:; echo "I am ${SHELL}"
:<<"::CMDLITERAL"
ECHO I am %COMSPEC%
::CMDLITERAL
:; echo "And ${SHELL} is back!"
:; exit
ECHO And back to %COMSPEC%
Depending on your needs or coding style, interlacing cmd
and sh
code may or may not make sense. Using heredocs is one method to perform such interlacing. This could, however, be extended with the GOTO
technique:
:<<"::CMDLITERAL"
@ECHO OFF
GOTO :CMDSCRIPT
::CMDLITERAL
echo "I can write free-form ${SHELL} now!"
if :; then
echo "This makes conditional constructs so much easier because"
echo "they can now span multiple lines."
fi
exit $?
:CMDSCRIPT
ECHO Welcome to %COMSPEC%
Universal comments, of course, can be done with the character sequence : #
or :;#
. The space or semicolon are necessary because sh
considers #
to be part of a command name if it is not the first character of an identifier. For example, you might want to write universal comments in the first lines of your file before using the GOTO
method to split your code. Then you can inform your reader of why your script is written so oddly:
: # This is a special script which intermixes both sh
: # and cmd code. It is written this way because it is
: # used in system() shell-outs directly in otherwise
: # portable code. See https://stackoverflow.com/questions/17510688
: # for details.
:; echo "This is ${SHELL}"; exit
@ECHO OFF
ECHO This is %COMSPEC%
Thus, some ideas and ways to accomplish sh
and cmd
-compatible scripts without serious side effects as far as I know (and without having cmd
output '#' is not recognized as an internal or external command, operable program or batch file.
).