-
-
Save kurahaupo/8130030 to your computer and use it in GitHub Desktop.
There's no excuse for using #!/usr/bin/env ...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# WARNING: if this file starts with #!/usr/bin/env, please use it to install itself, | |
# and then use that to install other files. | |
# | |
# 1. No write permission on runnable programs | |
# 2. No partially-written files during installation; pivot into place once complete | |
# 3. No .sh etc suffices on runnable programs | |
# 4. Set the #! line at installation, to use a set path, not /usr/bin/env | |
# | |
verbose=0 | |
sources=() | |
bindir= | |
declare -A interp=( | |
[.pl]=perl | |
[.py]=python | |
[.pyc]=python | |
[bash]=$BASH | |
[sh]=/bin/sh | |
) | |
default_lang=.bash | |
owner= | |
group= | |
mybin=$HOME/bin | |
sysbin=/usr/local/bin | |
match_prefix() { | |
local i= j= k= n=0 | |
k=$1 ; shift | |
for i do [[ "$i" = "$k"* ]] && { j=$i ; ((++n)) ; } ; done | |
if ((n==1)) ; then printf '%s' "$j" ; else printf '%s' "$k" ; false ; fi | |
} | |
while (($#)) | |
do case $( match_prefix "$1" --help --admin --dir --group --interpreter --owner --quiet --user --verbose --bash --perl --php --python ) in | |
(-h|--help) cat <<EndOfHelp ; exit 0 ;; | |
$0 [options | files] ... | |
--admin --me | |
--bindir PATH | |
--group GID | |
--owner UID | |
--verbose --quiet | |
EndOfHelp | |
(-B|--bash) interp[bash]=$2 ; shift ;; | |
(-H|--php) interp[php]=$2 ; shift ;; | |
(-P|--perl) interp[perl]=$2 ; shift ;; | |
(-S|--sh) interp[sh]=$2 ; shift ;; | |
(-Y|--python) interp[python]=$2 ; shift ;; | |
(-a|--admin) bindir=$sysbin ; : ${owner:=root} ${group:=root} ;; | |
(-b|--bindir) bindir=$2 ; shift ;; | |
(-g|--group) group=$2 ; shift ;; | |
(-i|--interpreter) interp[${2##*/}]=$2 ; shift ;; | |
(-m|--me) bindir=$mybin owner= group= ;; | |
(-o|--owner) owner=$2 ; shift ;; | |
(-q|--quiet) verbose=0 _v= _vv= ;; | |
(-v|--verbose) ((++verbose)) ; _vv=$_v _v=${_v:--}v ;; | |
# Don't change bundled-options boiler-plate, except for ──╮ | |
# ╭──── the list of option letters which take args ←────╯ | |
# ↓ | |
(-[dgio]?*) set x "${1:0:2}" "${1:2}" "${@:2}" ;; | |
(-[^-]?*) set x "${1:0:2}" "-${1:2}" "${@:2}" ;; | |
(--*=*) set x "${1%%=*}" "${1#*=}" "${@:2}" ;; | |
# boiler-plate error handling | |
(-*) echo >&2 "Invalid option '$1'" ; exit 64 ;; | |
# permutable non-option args... | |
(*) sources+=("$1") ;; | |
esac | |
shift | |
done | |
: ${bindir:=$mybin} | |
for p in "${sources[@]}" | |
do | |
n=${p##*/} | |
r=${n%%.*} | |
s=${n#"$r"} | |
t=$bindir/$r | |
tt=$bindir/.$n.$$ | |
read -r l < "$p" || break | |
if [[ $l = '#!'* ]] | |
then | |
# use basename of existing #! line | |
i=${l#'#!'} | |
i=${i#"${i%%[^ ]*}"} | |
i=${i#/usr/bin/env} | |
i=${i#"${i%%[^ ]*}"} | |
i=${i%%' '*} | |
i=${i##*/} | |
((verbose>1)) && echo "hashbang filetype $i" | |
else | |
# look up suffix | |
i=${interp[${s:-$default_lang}]:=${s#.}} | |
((verbose>1)) && echo "suffix filetype $i" | |
fi | |
# if not an absolute path, look up in $PATH (and cache result) | |
[[ $i = /* ]] || | |
i=${interp[$i]:=$(type -p "$i")} || break | |
((verbose>1)) && echo "filetype path $i" | |
[[ $p -ef $t ]] && { | |
echo "Source '$p' and target '$t' are same file; won't overwrite" | |
continue | |
} | |
sed -e " | |
1{ | |
s@^#! */[^ ]*/${i##*/} *\( [^ ].*|\)@#!$i\1@;tz; | |
s@^#! */usr/bin/env *${i##*/}@#!$i@;tz; | |
s@^@#!$i\n@;tz; | |
:z | |
} | |
" < "$p" > "$tt" || { | |
echo "Cannot create '$tt'" | |
continue | |
} | |
if | |
chmod 555 "$tt" && | |
{ [[ -z $owner$group ]] || | |
chown $owner:$group "$tt" ; } && | |
mv -f "$tt" "$t" | |
then | |
((verbose)) && echo "Installed '$p' as '$t' with interpreter '$i'" | |
else | |
rm -f "$tt" | |
echo "Could not install '$p' as '$t'" | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment