My Notebook

Polymorphic file that is a valid Windows-Batch, Bourne-Shell and Powershell script

Author
Date
Category
Scripting

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

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

Result on Linux

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

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