The following file is a valid Windows-Batch, Bourne-Shell and Powershell script all at the same time. It probably isn't very useful, but I find polymorphic files really fascinating in and off themselves. I created it by trail and error, so there is probably a nicer version of something like this somewhere on the Internet.
Step 1: Windows-Batch and Bourne-Shell
The first step is to create a polymorphic script that is both a Windows-Batch and Bourne-Shell script. This is relatively easy:
Source Code
#!/bin/sh
: << "BATCHFILE"
@echo off
cls
echo Windows: Batch-file
echo.
echo Count to 10:
echo.
for /l %%i in (1, 1, 10) do echo Count %%i
goto:EOF
BATCHFILE
echo "Unix: Bourne-Shell"
echo -e "\nCount to 10:\n"
for i in {1..10}; do
echo "Count $i"
done
Execution as Windows-Batch file
The hashbang (#!/bin/sh
) in the first line actually causes an error when executed as a Windows-Batch file, but the execution just continues to the next line and the error message is hidden afterwards by the cls
command.
Windows-Batch files use the colon :
to define a label. The line : << "BATCHFILE"
is clearly not a correct label, but the interpreter accepts it without any errors. I tried several ways to jump to this weird label, but it seems to be impossible. The command goto:EOF
terminates the Batch script and everything after it is ignored by the interpreter.
Execution in a Bourne-Shell
In a Bourne-Shell the colon :
is a built-in Noop-command. It does nothing, takes no arguments and always returns zero. The code << "BATCHFILE"
starts a Here-document, whose content is piped to the Noop-command. All of the Windows-Batch code is contained in the Here-document and therefore ignored by the Bourne-Shell. The double quotes around "BATCHFILE"
are very important, because they prevent variable expansion inside the Here-Document.
Step 2: Powershell support
The second step is to add Powershell support, which makes things more complicated:
Source Code
#!/bin/sh
function REM() { return; }
REM @'
REM '; : << "BATCHFILE"
@echo off
cls
echo Windows: Batch-file
echo.
echo Count to 10:
echo.
for /l %%i in (1, 1, 10) do echo Count %%i
goto:EOF
BATCHFILE
echo "Unix: Bourne-Shell"
echo -e "\nCount to 10:\n"
for i in {1..10}; do
echo "Count $i"
done
exit
'@
Write-Host "Windows: Powershell"
Write-Host "`r`nCount to 10:`r`n"
for ($i=1; $i -le 10; $i++) {
Write-Host "Count $i"
}
Execution as Windows-Batch file
The hashbang (#!/bin/sh
) and the word function
causes an error when executed as a Windows-Batch file, but the execution continues and the error messages are hidden afterwards by the cls
command.
Windows-Batch files use the REM
keyword to mark a one line comment, so all lines beginning with REM
are ignored. Everything else works as before.
Execution in a Bourne-Shell
On the third line the function REM
is declared, which does nothing and takes no arguments. On the fourth line the function REM
is called with the string '@\nREM '
as its first argument. Everything else works as before. The command exit
terminates the interpreter and everything after it is ignored.
Execution in the Windows-Powershell
On the third line the function REM
is declared, which does nothing and takes no arguments. On the fourth line the function REM
is called with a multiline string as its first argument. A multiline string starts with @'
and ends with '@
. It is important to use the single quote version here, because it prevents variable expansion. All of the code for the Batch-file and Bourne-Shell is contained in this string and therefore ignored.
Conclusion
A polymorphic script like this is not very useful, because Windows will only execute it, if it has the file extension .bat
or .ps1
, but nobody would try to execute such a file as a Shell-script on a Unix-like system.
References
- Bourne Shell Builtins - https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html
- Wikipedia on Here-document - https://en.wikipedia.org/wiki/Here_document