nix icon indicating copy to clipboard operation
nix copied to clipboard

Make `nix-shell` shebang compatible with all POSIX systems

Open DPDmancul opened this issue 3 years ago • 0 comments

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.

DPDmancul avatar Sep 22 '22 09:09 DPDmancul