Bash+Batch In One File

Today I wanted to write a script that I could execute from both *nix (using Bash or a similar shell) and on Windows (from a command prompt, i.e. a batch file). I found Max Norin’s solution which works, but has a few limitations, e.g. when run it outputs either the word “off” when run in *nix or the word “goto” when run on Windows. There’s got to be a sneaky solution, right?

Here’s my improved version:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@goto(){
  # Linux code here
  uname -o
}

@goto $@
exit

:(){
@echo off
rem Windows script here
echo %OS%

Mine exploits the fact that batch files can prefix commands with @ to suppress outputting them as they execute. So @goto can be a valid function name in bash/zsh etc. but is interpreted as a literal goto command in a Windows Command Prompt. This allows me to move the echo off command – which only has meaning to Windows – into the Windows section of the script and suppress it with @.

The file above can be saved as e.g. myfile.cmd and will execute in a Windows Command Prompt (or in MS-DOS) or your favourite *nix OS. Works in MacOS/BSD too, although obviously any more-sophisticated script is going to have to start working around the differences between GNU and non-GNU versions of core utilities, which is always a bit of a pain! Won’t work in sh because you can’t define functions like that.

But the short of it is you can run this on a stock *nix OS and get:

$ ./myfile.cmd
GNU/Linux

And you can run it on Windows and get:

> .\myfile.cmd
Windows_NT

You can’t put a shebang at the top because Windows hates it, but there might be a solution using PowerShell scripts (which use hashes for comments: that’s worth thinking about!). In any case: if the interpreter strictly matters you’ll probably want to shell to it on line 3 with e.g. bash -c.

Why would you want such a thing? I’m not sure. But there is is, if you do.