Last active
June 2, 2025 20:17
-
-
Save jdarpinian/6860ddfd92b5b458a20ab6055583bc3e to your computer and use it in GitHub Desktop.
Polyglot files that can run on Windows, Linux, and Mac using bash, cmd, and C.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 0 // 2>NUL & GOTO :startbatch | |
COMPILER_OPTIONS="-g -Wall -Wextra --std=c99 -O1";THIS_FILE="$(cd "$(dirname "$0")"; pwd -P)/$(basename "$0")";OUT_FILE="/tmp/build-cache/$THIS_FILE";mkdir -p "$(dirname "$OUT_FILE")";test "$THIS_FILE" -ot "$OUT_FILE" || $(which clang || which gcc) $COMPILER_OPTIONS -xc "$THIS_FILE" -o "$OUT_FILE" || exit;exec "$OUT_FILE" "$@" | |
:startbatch | |
@echo off | |
setlocal enableextensions enabledelayedexpansion | |
md %TEMP%%~p0 2>NUL | |
rem Use xcopy to test if this file is newer than the cached executable. | |
for /f %%i in ('xcopy %0 %TEMP%%~pnx0.exe /D /Y /Q') do set copied=%%i | |
if "%copied:~0,1%" neq "0" ( | |
rem Search for Visual Studio env vars. These are set globally if VS <2017 is installed. | |
for /L %%i in (60,10,140) DO ( | |
set "vs=VS%%i%COMNTOOLS" | |
if defined !vs! ( | |
set "vsfound=!vs!" | |
) | |
) | |
) | |
if "%copied:~0,1%" neq "0" ( | |
rem If cl.exe is already in the PATH, use it. Otherwise, look for VS 2017, then older versions. | |
for %%X in (cl.exe) do (set FOUND_COMPILER=%%~$PATH:X) | |
if not defined FOUND_COMPILER ( | |
rem Look for VS 2017 using its start menu link, because Microsoft has broken every other reasonable way of finding it. | |
set "VS_START_MENU=%ProgramData%\Microsoft\Windows\Start Menu\Programs\Visual Studio 2017.lnk" | |
if exist !VS_START_MENU! ( | |
rem Use "wmic" tool to extract the devenv.exe path from the .lnk file, then run vcvars64.bat | |
for /f "delims=" %%I in ('wmic path win32_shortcutfile where "name='!VS_START_MENU:\=\\!'" get target /value') do for /f "delims=" %%# in ("%%~I") do set "%%~#" | |
pushd | |
call "!target!\..\..\..\VC\Auxiliary\Build\vcvars64.bat" >NUL 2>NUL | |
popd | |
) else ( | |
if not defined vsfound ( | |
echo Could not find a Visual Studio installation. | |
exit /b 1 | |
) | |
if not exist "!%vsfound%!vsvars32.bat" ( | |
echo Your Visual Studio does not have a vsvars32.bat | |
) | |
call "!%vsfound%!vsvars32.bat" >NUL 2>NUL | |
for %%X in (cl.exe) do (set FOUND_COMPILER=%%~$PATH:X) | |
if not defined FOUND_COMPILER ( | |
echo Could not find a Visual Studio installation with a C compiler. | |
exit /b 1 | |
) | |
) | |
) | |
cl /nologo /TC /Fe:%TEMP%%~pnx0.exe %0 >NUL 2>NUL || exit /B %ERRORLEVEL% | |
) | |
%TEMP%%~pnx0.exe %* | |
exit /B | |
#endif | |
#include <stdio.h> | |
int main(int argc, char **argv) { | |
printf("Hello world!\n"); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
echo off & echo ; wait ; echo -ne "\e[1A\e[K" # > NUL | |
echo ; function goto { true; } ; function rem { true; } ; echo -ne "\e[1A\e[K\e[1A\e[K" # > NUL | |
goto startbatch | |
echo running in bash | |
exit | |
:startbatch | |
echo [6A[K[1B[K[1B[K[1B[K[1B[K[1B[K[6A | |
echo running in cmd | |
exit /B |
I've tested my version with both windows XP, and Wine's cmd
the 2>/dev/null=2>NUL
in the shebang is making sure that both wine and real cmd will not error on the shebang, it doesn't affect anything else, and it's also why you need -S
if you don't care about shebang erroring on windows you can remove that and use a normal #!/bin/sh
or #!/bin/bash
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@TomasMalecek there's actually no reason to bother making a dummy function for @ or anything, just redirect errors, and no more errors!
the script itself should be posix, but it requires /bin/env that supports -S for the shebang to work (gnu one works, which is default in most places, but it's not posix, and busybox and toybox's env do not work)
or you can just run it directly using dash/ash/bash or whatever other bourne-style shell you like, ignoring the shebang entirely ;P