Make `nix-shell` shebang compatible with all POSIX systems
nix-shell shebang (or the possible forthcoming more general nix shebang) is very useful to specify the dependencies of a script without creating a whole shell.nix file, but has the great disadvantage to make the script incompatible with systems not running Nix.
It would be so interesting to change the syntax used for nix-shell shebang in order to make it POSIX compatible (obviously only for the selection of the interpreter, the dependencies will only be available for Nix).
Since POSIX shebang is very limited (it accepts only a full path to executable, usually /usr/bin/env with an argument, the real interpreter) it seems hard to find a good solution.
I thought of a possible wrapping of the env executable:
#! /bin/sh
if grep -q "^#! \?nix-shell" "$2"; then
# if the script has a nix-shell shebang run it with nix-shell;
# before that add the interpreter to the shebang
TMP=$(mktemp)
sed "s/\(^#! \?nix-shell\)/\1 -i $1/" "$2" > "$TMP"
nix-shell "$TMP"
rm "$TMP"
else
# otherwise run the real env
env "$@"
fi
the first problem that arises is the creation of a patched file with sed. This file will not have the same path of the file we want to execute. This could be easily solved by allowing nix-shell to accept -i also outside shebangs: in this way our patched env would become
#! /bin/sh
if grep -q "^#! \?nix-shell" "$2"; then
nix-shell -i "$1" "$2"
else
env "$@"
fi
An example of script with this new nix-shell shebang:
#! /usr/bin/env python3
#! nix-shell -p python3Packages.numpy
import numpy as np
print(np.ones(2))
Here a script to test this wrapper:
#! /bin/sh
PATCHED_ENV=$(mktemp)
cat > "$PATCHED_ENV" << EOF
#! /bin/sh
if grep -q "^#! \?nix-shell" "\$2"; then
TMP=\$(mktemp)
sed "s/\(^#! \?nix-shell\)/\1 -i \$1/" "\$2" > "\$TMP"
nix-shell "\$TMP"
rm "\$TMP"
else
env "\$@"
fi
EOF
chmod +x "$PATCHED_ENV"
TEST_FILE=$(mktemp)
cat > "$TEST_FILE" << EOF
#! $PATCHED_ENV python3
#! nix-shell -p python3Packages.numpy
import numpy as np
print(np.ones(2))
EOF
TEST_FILE2=$(mktemp)
cat > "$TEST_FILE2" << EOF
#! $PATCHED_ENV python3
import numpy as np
print(np.ones(2))
EOF
chmod +x "$TEST_FILE" "$TEST_FILE2"
"$TEST_FILE" # script with new nix-shell shebang, but POSIX compatible
"$TEST_FILE2" # generic script without nix-shell shebang
rm "$TEST_FILE" "$TEST_FILE2" "$PATCHED_ENV"
Maybe it could be a problem if we use env outside shebangs and its second argument is a file containing a nix-shell shebang. I don't know how to solve this, and so my solution would be infeasible, but it is a starting point to allow writing script executable in all POSIX systems and not only in those with Nix.
Another problem is that patching env is very easy to do in NixOs, but maybe it can be difficult or impossible in other systems with only Nix package manager.