| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #!/bin/sh | 
					
						
							| 
									
										
										
										
											2016-05-11 21:22:25 -07:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2019-12-30 22:36:56 -08:00
										 |  |  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | 
					
						
							| 
									
										
										
										
											2018-10-07 13:52:23 -07:00
										 |  |  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | 
					
						
							| 
									
										
										
										
											2016-05-11 21:22:25 -07:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2018-10-07 13:52:23 -07:00
										 |  |  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # | 
					
						
							|  |  |  | # `sbang`: Run scripts with long shebang lines. | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # Many operating systems limit the length and number of possible | 
					
						
							|  |  |  | # arguments in shebang lines, making it hard to use interpreters that are | 
					
						
							|  |  |  | # deep in the directory hierarchy or require special arguments. | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # `sbang` can run such scripts, either as a shebang interpreter, or | 
					
						
							|  |  |  | # directly on the command line. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Usage | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # ----- | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # Suppose you have a script, long-shebang.sh, like this: | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #     1    #!/very/long/path/to/some/interp | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | #     2 | 
					
						
							|  |  |  | #     3    echo "success!" | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Invoking this script will result in an error on some OS's.  On | 
					
						
							|  |  |  | # Linux, you get this: | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #     $ ./longshebang.sh | 
					
						
							|  |  |  | #     -bash: ./longshebang.sh: /very/long/path/to/some/interp: bad interpreter: | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | #            No such file or directory | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # On macOS, the system simply assumes the interpreter is the shell and | 
					
						
							|  |  |  | # tries to run with it, which is not likely what you want. | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # `sbang` on the command line | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # --------------------------- | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # 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 | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # -------------------------- | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # You can also use `sbang` *as* the interpreter for your script. Put | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # `#!/bin/sh /path/to/sbang` on line 1, and move the original | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # shebang to line 2 of the script: | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #     1    #!/bin/sh /path/to/sbang | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | #     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 | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # operating systems like Mac OS X require the interpreter to be a binary, | 
					
						
							|  |  |  | # so it's best to use `sbang` as an argument to `/bin/sh`. Obviously, for | 
					
						
							|  |  |  | # this to work, `sbang` needs to have a short enough path that *it* will | 
					
						
							|  |  |  | # run without hitting OS limits. | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-27 14:11:43 +09:00
										 |  |  | # For Lua, node, and php scripts, the second line can't start with #!, as | 
					
						
							|  |  |  | # # is not the comment character in these languages (though they all | 
					
						
							|  |  |  | # ignore #! on the *first* line of a script). So, instrument such scripts | 
					
						
							|  |  |  | # like this, using --, //, or <?php ... ?> instead of # on the second | 
					
						
							|  |  |  | # line, e.g.: | 
					
						
							| 
									
										
										
										
											2016-06-23 00:03:23 -07:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #     1    #!/bin/sh /path/to/sbang | 
					
						
							| 
									
										
										
										
											2016-06-23 00:03:23 -07:00
										 |  |  | #     2    --!/long/path/to/lua with arguments | 
					
						
							| 
									
										
										
										
											2020-10-27 14:11:43 +09:00
										 |  |  | #     3    print "success!" | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #     1    #!/bin/sh /path/to/sbang | 
					
						
							| 
									
										
										
										
											2020-10-27 14:11:43 +09:00
										 |  |  | #     2    //!/long/path/to/node with arguments | 
					
						
							|  |  |  | #     3    print "success!" | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | #     1    #!/bin/sh /path/to/sbang | 
					
						
							| 
									
										
										
										
											2020-10-27 14:11:43 +09:00
										 |  |  | #     2    <?php #/long/path/to/php with arguments ?> | 
					
						
							|  |  |  | #     3    <?php echo "success!\n"; ?> | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # | 
					
						
							|  |  |  | # How it works | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # ------------ | 
					
						
							|  |  |  | # `sbang` is a very simple posix shell 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. | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # We disable two shellcheck errors below: | 
					
						
							|  |  |  | # SC2124: when saving arguments, we intentionally assign as an array | 
					
						
							|  |  |  | # SC2086: when splitting $shebang_line and exec args, we want to expand args | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Generic error handling | 
					
						
							|  |  |  | die() { | 
					
						
							|  |  |  |     echo "$@" 1>&2; | 
					
						
							|  |  |  |     exit 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # set SBANG_DEBUG to make the script print what would normally be executed. | 
					
						
							|  |  |  | exec="exec" | 
					
						
							|  |  |  | if [ -n "${SBANG_DEBUG}" ]; then | 
					
						
							|  |  |  |     exec="echo " | 
					
						
							|  |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # First argument is the script we want to actually run. | 
					
						
							|  |  |  | script="$1" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # ensure that the script actually exists | 
					
						
							|  |  |  | if [ -z "$script" ]; then | 
					
						
							|  |  |  |     die "error: sbang requires exactly one argument" | 
					
						
							|  |  |  | elif [ ! -f "$script" ]; then | 
					
						
							|  |  |  |     die "$script: no such file or directory" | 
					
						
							|  |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | # Search the first two lines of script for interpreters. | 
					
						
							|  |  |  | lines=0 | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | while read -r line && [ $lines -ne 2 ]; do | 
					
						
							|  |  |  |     if [ "${line#\#!}" != "$line" ]; then | 
					
						
							|  |  |  |         shebang_line="${line#\#!}" | 
					
						
							|  |  |  |     elif [ "${line#//!}" != "$line" ]; then      # // comments | 
					
						
							|  |  |  |         shebang_line="${line#//!}" | 
					
						
							|  |  |  |     elif [ "${line#--!}" != "$line" ]; then      # -- lua comments | 
					
						
							|  |  |  |         shebang_line="${line#--!}" | 
					
						
							|  |  |  |     elif [ "${line#<?php\ }" != "$line" ]; then  # php comments | 
					
						
							|  |  |  |         shebang_line="${line#<?php\ \#!}" | 
					
						
							|  |  |  |         shebang_line="${shebang_line%\ ?>}" | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  |     fi | 
					
						
							|  |  |  |     lines=$((lines+1)) | 
					
						
							|  |  |  | done < "$script" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | # shellcheck disable=SC2124 | 
					
						
							|  |  |  | # this saves arguments for later and intentionally assigns as an array | 
					
						
							|  |  |  | args="$@" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # handle scripts with sbang parameters, e.g.: | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #   #!/<spack-long-path>/perl -w | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # put the shebang line with all the parameters in the $@ array and get | 
					
						
							|  |  |  | # the first element. | 
					
						
							|  |  |  | # shellcheck disable=SC2086 | 
					
						
							|  |  |  | set $shebang_line | 
					
						
							|  |  |  | set -- "$@" | 
					
						
							|  |  |  | interpreter="$1" | 
					
						
							|  |  |  | arg1="$2" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # error if we did not find any interpreter | 
					
						
							|  |  |  | if [ -z "$interpreter" ]; then | 
					
						
							|  |  |  |     die "error: sbang found no interpreter in $script" | 
					
						
							|  |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Determine if the interpreter is a particular program, accounting for the | 
					
						
							|  |  |  | # '#!/usr/bin/env PROGRAM' convention. So: | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #     interpreter_is perl | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # will be true for '#!/usr/bin/perl' and '#!/usr/bin/env perl' | 
					
						
							|  |  |  | interpreter_is() { | 
					
						
							|  |  |  |     if [ "${interpreter##*/}" = "$1" ]; then | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  |     elif [ "$interpreter" = "/usr/bin/env" ] && [ "$arg1" = "$1" ]; then | 
					
						
							|  |  |  |         return 0 | 
					
						
							| 
									
										
										
										
											2016-09-22 09:43:47 +02:00
										 |  |  |     else | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  |         return 1 | 
					
						
							| 
									
										
										
										
											2016-09-22 09:43:47 +02:00
										 |  |  |     fi | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if interpreter_is "sbang"; then | 
					
						
							|  |  |  |     die "error: refusing to re-execute sbang to avoid infinite loop." | 
					
						
							|  |  |  | fi | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Finally invoke the real shebang line | 
					
						
							|  |  |  | # ruby and perl need -x to ignore the first line of input (the sbang line) | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | if interpreter_is perl || interpreter_is ruby; then | 
					
						
							|  |  |  |     # shellcheck disable=SC2086 | 
					
						
							|  |  |  |     $exec $shebang_line -x "$args" | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | else | 
					
						
							| 
									
										
										
										
											2020-10-26 11:06:03 -07:00
										 |  |  |     # shellcheck disable=SC2086 | 
					
						
							|  |  |  |     $exec $shebang_line "$args" | 
					
						
							| 
									
										
										
										
											2016-03-05 04:18:48 -08:00
										 |  |  | fi |