Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shebang: use interpreter relative to the script path

Tags:

python

shebang

I try to build scripts that work everywhere and always. For this I use a custom-built python, which is always in the parent directory relative to the script.

This way I could load my package on an USB-stick and it would work everywhere, regardless of where the stick is mounted and whether python is installed or not.

However, when I use

#!../python

then it works only when the script gets invoked from its directory, which is of course not acceptable.

Is there a way to do this or is this impossible in the current shebang-mechanism?

like image 584
Robby75 Avatar asked Nov 20 '13 11:11

Robby75


2 Answers

There is a healthy set of multi-line shebang scripts on this page for a lot of languages, example:

#!/bin/sh
"exec" "`dirname $0`/python" "$0" "$@"
print copyright

And if you want one-line shebang, this answer (and question) explains the issue in the details and suggests the following approaches using additional scripts inside the shebang:

Using AWK

#!/usr/bin/awk BEGIN{a=ARGV[1];sub(/[a-z_.]+$/,"python",a);system(a"\t"ARGV[1])}

Using Perl

#!/usr/bin/perl -e$_=$ARGV[0];exec(s/\w+$/python/r,$_)

update from 11Jan21:

Using updated env utility:

$ env --version | grep env
env (GNU coreutils) 8.30
$ env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
Set each NAME to VALUE in the environment and run COMMAND.

Mandatory arguments to long options are mandatory for short options too.
  -i, --ignore-environment  start with an empty environment
  -0, --null           end each output line with NUL, not newline
  -u, --unset=NAME     remove variable from the environment
  -C, --chdir=DIR      change working directory to DIR
  -S, --split-string=S  process and split S into separate arguments;
                        used to pass multiple arguments on shebang lines

So, passing -S to env will do the job now

like image 181
Anton Avatar answered Oct 29 '22 16:10

Anton


Expanding @Anton's answer so as not to fail on spaces and other special characters in the path and to explain a bit more about the magic.

#!/bin/sh
"true" '''\'
exec "$(dirname "$(readlink -f "$0")")"/venv/bin/python "$0" "$@"
'''

__doc__ = """You will need to deliberately set your docstrings though"""

print("This script is interpretable by python and sh!")

This clever script is comprehendable by both sh and python. Each of which react to it differently. The first 2 lines after the shebang are interpreted by sh and cause it to hand over exec to a relative python binary (supplied with the same command line arguments). These same lines are safely discarded by python since they amount to a string ("true") followed by a multi-line string (''').

A little more about this subject can be read here.

like image 11
jeffre Avatar answered Oct 29 '22 14:10

jeffre