summaryrefslogtreecommitdiff
path: root/bin/sbang
blob: e71074b3302746bed51cc2db62da9a4c936c2365 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/bin/bash
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
#
# `sbang`: Run scripts with long shebang lines.
#
# Many operating systems limit the length of shebang lines, making it
# hard to use interpreters that are deep in the directory hierarchy.
# `sbang` can run such scripts, either as a shebang interpreter, or
# directly on the command line.
#
# Usage
# -----------------------------
# Suppose you have a script, long-shebang.sh, like this:
#
#     1    #!/very/long/path/to/some/interpreter
#     2
#     3    echo "success!"
#
# Invoking this script will result in an error on some OS's.  On
# Linux, you get this:
#
#     $ ./long-shebang.sh
#     -bash: ./long: /very/long/path/to/some/interp: bad interpreter:
#            No such file or directory
#
# On Mac OS X, the system simply assumes the interpreter is the shell
# and tries to run with it, which is likely not what you want.
#
#
# `sbang` on the command line
# -----------------------------
# You can use `sbang` in two ways.  The first is to use it directly,
# from the command line, like this:
#
#     $ sbang ./long-shebang.sh
#     success!
#
#
# `sbang` as the interpreter
# -----------------------------
# You can also use `sbang` *as* the interpreter for your script. Put
# `#!/bin/bash /path/to/sbang` on line 1, and move the original
# shebang to line 2 of the script:
#
#     1    #!/bin/bash /path/to/sbang
#     2    #!/long/path/to/real/interpreter with arguments
#     3
#     4    echo "success!"
#
#     $ ./long-shebang.sh
#     success!
#
# On Linux, you could shorten line 1 to `#!/path/to/sbang`, but other
# operating systems like Mac OS X require the interpreter to be a
# binary, so it's best to use `sbang` as a `bash` argument.
# Obviously, for this to work, `sbang` needs to have a short enough
# path that *it* will run without hitting OS limits.
#
# For Lua, scripts the second line can't start with #!, as # is not
# the comment character in lua (even though lua ignores #! on the
# *first* line of a script).  So, instrument a lua script like this,
# using -- instead of # on the second line:
#
#     1    #!/bin/bash /path/to/sbang
#     2    --!/long/path/to/lua with arguments
#     3
#     4    print "success!"
#
# How it works
# -----------------------------
# `sbang` is a very simple bash script. It looks at the first two
# lines of a script argument and runs the last line starting with
# `#!`, with the script as an argument. It also forwards arguments.
#

# First argument is the script we want to actually run.
script="$1"

# Search the first two lines of script for interpreters.
lines=0
while read line && ((lines < 2)) ; do
    if [[ "$line" = '#!'* ]]; then
        interpreter="${line#\#!}"
    elif [[ "$line" = '--!'*lua* ]]; then
        interpreter="${line#--!}"
    fi
    lines=$((lines+1))
done < "$script"

# Invoke any interpreter found, or raise an error if none was found.
if [[ -n "$interpreter" ]]; then
    if [[ "${interpreter##*/}" = "perl" ]]; then
        exec $interpreter -x "$@"
    else
        exec $interpreter "$@"
    fi
else
    echo "error: sbang found no interpreter in $script"
    exit 1
fi