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 figu
you can try this:
#|| goto :batch_part
echo $PATH
#exiting the bash part
exit
:batch_part
echo %PATH%
Probably you'll need to use /r/n
as a new line instead of a unix style.If I remember correct the unix new line is not interpreted as a new line by .bat
scripts.Another way is to create an #.exe
file in the path that does do nothing in similar manner as my answer here: Is it possible to embed and execute VBScript within a batch file without using a temporary file?
EDIT
The binki's answer is almost perfect but still can be improved:
:<<BATCH
@echo off
echo %PATH%
exit /b
BATCH
echo $PATH
It uses again the :
trick and the multi line comment.Looks like cmd.exe (at least on windows10) works without problems with the unix style EOLs so be sure that your script is converted into linux format. (same approach has been seen used before here and here ) . Though using shebang still will produce redundant output...
The previous answers seem to cover pretty much all the options and helped me a lot. I'm including this answer here just to demonstrate the mechanism I used to include both a Bash script and a Windows CMD script in the same file.
echo >/dev/null # >nul & GOTO WINDOWS & rem ^
echo 'Processing for Linux'
# ***********************************************************
# * NOTE: If you modify this content, be sure to remove carriage returns (\r)
# * from the Linux part and leave them in together with the line feeds
# * (\n) for the Windows part. In summary:
# * New lines in Linux: \n
# * New lines in Windows: \r\n
# ***********************************************************
# Do Linux Bash commands here... for example:
StartDir="$(pwd)"
# Then, when all Linux commands are complete, end the script with 'exit'...
exit 0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
:WINDOWS
echo "Processing for Windows"
REM Do Windows CMD commands here... for example:
SET StartDir=%cd%
REM Then, when all Windows commands are complete... the script is done.
The first line (echo >/dev/null # >nul & GOTO WINDOWS & rem ^
) will be ignored and the script will flow through each line immediately following it until the exit 0
command is executed. Once exit 0
is reached, the script execution will end, ignoring the Windows commands below it.
The first line will execute the GOTO WINDOWS
command, skipping the Linux commands immediately following it and continuing execution at the :WINDOWS
line.
Since I was editing this file in Windows, I had to systematically remove the carriage returns (\r) from the Linux commands or else I got abnormal results when running the Bash portion. To do this, I opened the file in Notepad++ and did the following:
Turn on the option for viewing end of line characters (View
> Show Symbol
> Show End of Line
). Carriage returns will then show as CR
characters.
Do a Find & Replace (Search
> Replace...
) and check the Extended (\n, \r, \t, \0, \x...)
option.
Type \r
in the Find what :
field and blank out the Replace with :
field so there's nothing in it.
Starting at the top of the file, click the Replace
button until all of the carriage return (CR
) characters have been removed from the top Linux portion. Be sure to leave the carriage return (CR
) characters for the Windows portion.
The result should be that each Linux command ends in just a line feed (LF
) and each Windows command ends in a carriage return and line feed (CR
LF
).
There is a platform independent build tools like Ant or Maven with xml syntax (based on Java). So, you could rewrite all your scripts in Ant or Maven an run them despite os type. Or you could just create Ant wrapper script, which will analyze os type and run appropriate bat or bash script.
You can share variables:
:;SET() { eval $1; }
SET var=value
:;echo $var
:;exit
ECHO %var%
I wanted to comment, but can only add an answer at the moment.
The techniques given are excellent and I use them also.
It is hard to retain a file which has two kinds of line breaks contained within it, that being /n
for the bash part and /r/n
for the windows part. Most editors try and enforce a common line break scheme by guessing what kind of file you are editing. Also most methods of transferring the file across the internet (particularly as a text or script file) will launder the line breaks, so you could start with one kind of line break and end up with the other. If you made assumptions about line breaks and then gave your script to someone else to use they might find it doesn't work for them.
The other problem is network mounted file systems (or CDs) that are shared between different system types (particularly where you can't control the software available to the user).
One should therefore use the DOS line break of /r/n
and also protect the bash script from the DOS /r
by putting a comment at the end of each line (#
). You also cannot use line continuations in bash because the /r
will cause them to break.
In this way whoever uses the script, and in whatever environment, it will then work.
I use this method in conjunction with making portable Makefiles!
There are several ways of executing different commands on bash
and cmd
with the same script.
cmd
will ignore lines that start with :;
, as mentioned in other answers. It will also ignore the next line if the current line ends with the command rem ^
, as the ^
character will escape the line break and the next line will be treated as a comment by rem
.
As for making bash
ignore the cmd
lines, there are multiple ways. I have enumerated some ways to do that without breaking the cmd
commands:
#
command (not recommended)If there is no #
command available on cmd
when the script is run, we can do this:
# 2>nul & echo Hello cmd! & rem ^
echo 'Hello bash!' #
The #
character at the beginning of the cmd
line makes bash
treat that line as a comment.
The #
character at the end of the bash
line is used to comment out the \r
character, as Brian Tompsett pointed out in his answer. Without this, bash
will throw an error if the file has \r\n
line endings, required by cmd
.
By doing # 2>nul
, we're tricking cmd
to ignore the error of some non-existent #
command, while still executing the command that follows.
Don't use this solution if there is a #
command available on the PATH
or if you have no control over the commands available to cmd
.
echo
to ignore the #
character on cmd
We can use echo
with it's output redirected to insert cmd
commands on bash
's commented out area:
echo >/dev/null # >nul & echo Hello cmd! & rem ^
echo 'Hello bash!' #
Since the #
character has no special meaning on cmd
, it is treated as a part of the text to echo
. All we had to do is redirect the output of the echo
command and insert other commands after it.
#.bat
fileecho >/dev/null # 1>nul 2> #.bat
# & echo Hello cmd! & del #.bat & rem ^
echo 'Hello bash!' #
The echo >/dev/null # 1>nul 2> #.bat
line creates an empty #.bat
file while on cmd
(or replaces existing #.bat
, if any), and does nothing while on bash
.
This file will be used by the cmd
line(s) that follows even if there is some other #
command on the PATH
.
The del #.bat
command on the cmd
-specific code deletes the file that was created. You only have to do this on the last cmd
line.
Don't use this solution if a #.bat
file could be on your current working directory, as that file will be erased.
cmd
commands on bash
:; echo 'Hello bash!';<<:
echo Hello cmd! & ^
:
By placing the ^
character at the end of the cmd
line we're escaping the line break, and by using :
as the here-document delimiter, the delimiter line contents will have no effect on cmd
. That way, cmd
will only execute its line after the :
line is over, having the same behaviour as bash
.
If you want to have multiple lines on both platforms and only execute them at the end of the block, you can do this:
:;( #
:; echo 'Hello' #
:; echo 'bash!' #
:; );<<'here-document delimiter'
(
echo Hello
echo cmd!
) & rem ^
here-document delimiter
As long as there is no cmd
line with exactly here-document delimiter
, this solution should work. You can change here-document delimiter
to any other text.
In all of the presented solutions, the commands will only be executed after the last line, making their behaviour consistent if they do the same thing on both platforms.
Those solutions must be saved to files with \r\n
as line breaks, otherwise they won't work on cmd
.