Single script to run on both Windows and Linux Bash?

Is it possible to write a single script file that runs on both Windows (processed as .bat) and Linux (via Bash)?

I know the basic syntax of both, but did not understand. It could probably use some Bash syntax, obscure or some kind of glitch in the Windows batch processor.

The command to execute can only be one line to execute other scripts.

The motivation is to have only one application download command for Windows and Linux.

Update: The need for a system “native” shell script is that it needs to select the correct version of the interpreter, match some known environment variables, etc. Installing additional environments, such as CygWin, is not preferable - I would like to leave the "download and run" concept.

The only other language for Windows is Windows Scripting Host - WSH, which is set to 98 by default.

+60
bash batch-file
Jul 07 '13 at 9:05
source share
7 answers

What I did was use cmds label syntax as a comment marker . The label character, a colon ( : , is equivalent to true in most POSIXish shells. If you immediately follow the label character with another character that cannot be used in GOTO , commenting on your cmd script should not affect your cmd code.

Hacking is to put lines of code after the character sequence " :; ". If you write mostly single-line scripts or, as it may be, you can write one sh line for many cmd lines, this might be ok. Do not forget that any use of $? should be before your next colon : because : resets $? at 0.

 :; echo "Hi, I'm ${SHELL}."; exit $? @ECHO OFF ECHO I'm %COMSPEC% 

A very contrived example of protecting $? :

 :; false; ret=$? :; [ ${ret} = 0 ] || { echo "Program failed with code ${ret}." >&2; exit 1; } :; exit ECHO CMD code. 

Another idea to skip the 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 heredocs separator is both specified (so that sh does not interpret any of its contents when working 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, alternating cmd and sh code may or may not make sense. Using heredocs is one way of doing this weave. However, this could be expanded using the GOTO method:

 :<<"::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 executed with a character sequence : # or :;# . A space or semicolon is necessary because sh considers # be part of the command name if it is not the first character of the identifier. For example, you can write generic comments on the first lines of a file before using the GOTO method to split code. Then you can tell your reader 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 execute sh and cmd compatible scripts without serious side effects, as far as I know (and without cmd output, output '#' is not recognized as an internal or external command, operable program or batch file. ).

+57
Jul 12 '13 at 20:44
source share

you can try the following:

 #|| goto :batch_part some unix commands #exiting the bash part exit :batch_part some windows commands 

You will probably need to use /r/n as a new line instead of the unix style. If I remember correctly, the new unix line is not interpreted as a new line by .bat scripts. Another way is to create #.exe in a way that does nothing similar to my answer here: Is it possible to embed and execute VBScript in a batch file without using a temporary file?

+15
Jul 07 '13 at 9:29
source share

I wanted to comment, but I can only add an answer at the moment.

The above methods are excellent, and I use them too.

It is difficult to save a file with two types of line breaks contained within it, which is /n for the bash part and /r/n for the Windows part. Most editors try to use the usual line break scheme, guessing which file you are editing. Also, most ways to transfer a file over the Internet (especially as text or a script file) launder line breaks, so you can start with one type of line break and end up with another. If you made assumptions about line breaks, and then passed your script to someone else, they might find that it does not work for them.

Another problem is network file systems (or CDs) that are shared between different types of systems (especially where you cannot manage the software available to the user).

Therefore, you should use the DOS /r/n line break, and also protect the bash script from DOS /r by putting a comment at the end of each line ( # ). You also cannot use line extensions in bash because /r will break them.

That way, anyone who uses the script, and in any environment, will work.

I use this method in conjunction with creating a portable makefile!

+9
Dec 17 '14 at 1:58
source share

The below works for me without errors or error messages with Bash 4 and Windows 10, unlike the answers above. I call the file "whatever.cmd", do chmod +x to make it executable on linux, and make it the end of the unix line ( dos2unix ) to keep Bash quiet.

 :; if [ -z 0 ]; then @echo off goto :WINDOWS fi if [ -z "$2" ]; then echo "usage: $0 <firstArg> <secondArg>" exit 1 fi # bash stuff exit :WINDOWS if [%2]==[] ( SETLOCAL enabledelayedexpansion set usage="usage: %0 <firstArg> <secondArg>" @echo !usage:"=! exit /b 1 ) :: windows stuff 
+5
Aug 19 '16 at 4:24
source share

There are several ways to execute various commands on bash and cmd with the same script.

cmd will ignore lines starting with :; as mentioned in other answers. It also ignores the next line if the current line ends with the rem ^ command, since the ^ character will exit the line break and the next line will be considered as a rem comment.

Regarding bash ignoring cmd lines, there are several ways. I have listed some ways to do this without breaking cmd commands:

Optional command # (not recommended)

If cmd does not have the # command when the script is executed, 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 this line as a comment.

The # character at the end of the bash line is used to comment on the \r character, as Brian Thompsett pointed out in his answer . Without this, bash will throw an error if the file has \r\n line endings required by cmd .

By executing # 2>nul , we trick cmd to ignore the error of some non-existent # command, but at the same time execute the following command.

Do not use this solution if PATH has a # command or if you do not have control over the commands available for cmd .




Using echo to ignore the # character on cmd

We can use echo with its output redirected to insert cmd commands into the bash scope:

 echo >/dev/null # >nul & echo Hello cmd! & rem ^ echo 'Hello bash!' # 

Since the # character has no special meaning in cmd , it is considered as part of the echo text. All we had to do was redirect the output of the echo command and insert other commands after it.




Empty #.bat file

 echo >/dev/null # 1>nul 2> #.bat # & echo Hello cmd! & del #.bat & rem ^ echo 'Hello bash!' # 

The line echo >/dev/null # 1>nul 2> #.bat creates an empty #.bat file during cmd (or replaces the existing #.bat , if any) and does nothing during bash .

This file will be used by the next line of cmd , even if there is another # command in PATH .

The del #.bat command in cmd specific code deletes the file that was created. You only need to do this in the last line of cmd .

Do not use this solution if the #.bat file may be in your current working directory, as this file will be deleted.




Recommended: use a document here to ignore cmd commands in bash

 :; echo 'Hello bash!';<<: echo Hello cmd! & ^ : 

By placing the ^ character at the end of the cmd line, we elude line breaks and using : as the separator here in the document, the contents of the separator line will not affect cmd . Thus, cmd will execute its line only after the end of the line : having the same behavior as bash .

If you want to have several lines on both platforms and execute them only 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 the here-document delimiter to any other text.




In all presented solutions, the commands will be executed only after the last line , making their behavior consistent if they do the same on both platforms.

These solutions should be saved in files with \r\n as line breaks, otherwise they will not work on cmd .

+1
Jul 27 '17 at 3:18
source share

You can share variables:

 :;SET() { eval $1; } SET var=value :;echo $var :;exit ECHO %var% 
0
04 Oct '17 at 12:44 on
source share

Platform-independent build tools exist, such as Ant or Maven, with xml syntax (based on Java). Thus, you can rewrite all your scripts in Ant or Maven and run them, despite the os type. Or you can simply create an Ant wrapper script that will parse the os type and run the appropriate bat or bash script.

-four
Jul 07 '13 at 9:10
source share



All Articles