#!/bin/sh # # Copyright 2013-2023 Lawrence Livermore National Security, LLC and other # sbang project developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) # # `sbang`: Run scripts with long shebang lines. # # 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. # # To use, put the long shebang on the second line of your script, and # make sbang the interpreter, like this: # # #!/bin/sh /path/to/sbang # #!/long/path/to/real/interpreter with arguments # # `sbang` will run the real interpreter with the script as its argument. # # See https://github.com/spack/sbang for more details. # # 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 # First argument is the script we want to actually run. script="$1" # 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 # Search the first two lines of script for interpreters. lines=0 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%\ ?>}" fi lines=$((lines+1)) done < "$script" # error if we did not find any interpreter if [ -z "$shebang_line" ]; then die "error: sbang found no interpreter in $script" fi # parse out the interpreter and first argument IFS=' ' read -r interpreter arg1 rest <<EOF $shebang_line EOF # 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 else return 1 fi } 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 "$@" else # shellcheck disable=SC2086 $exec $shebang_line "$@" fi