-
-
Save d630/d07faa982a1dc853a149946204e1b543 to your computer and use it in GitHub Desktop.
posix shell: understanding command search and execution
This file contains 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
#!/bin/sh | |
get_path () { | |
command -v "$1"; | |
}; | |
has_operator () { | |
case "$1" in | |
(*";"*|*"&"*|*"&&"*|*"||"*|*";;"*|*"("*|*")"*) | |
command \return 1;; | |
(*"<"*|*">"*|*"<<"*|*">>"*|*"<&"*|*">&"*|*"<>"*|*"<<-"*|*">|"*) | |
command \return 2;; | |
esac; | |
}; | |
has_slash () { | |
[ "${1##*/*}" = "$1" ]; | |
}; | |
is_builtin () { | |
[ "$1" = "built-in" ]; | |
}; | |
is_function () { | |
[ "$1" = "function" ]; | |
}; | |
is_reserved_word () { | |
case "$1" in | |
(if|then|else|elif|fi|do|done| \ | |
case|esac|while|until|for| \ | |
\{|\}|\!) | |
command \return 1;; | |
(\[\[|\]\]|function|select|?*:) | |
command \return 2;; | |
esac; | |
}; | |
is_special_builtin () { | |
case "$1" in | |
(break|:|continue|.|eval|exec|exit|export|readonly|return|set|shift| \ | |
times|trap|unset) | |
command \return 1;; | |
esac; | |
}; | |
is_unspecified_utility () { | |
case "$1" in | |
( alloc|autoload|bind|bindkey|builtin|bye|caller|cap|chdir|clone| \ | |
comparguments|compcall|compctl|compdescribe|compfiles|compgen| \ | |
compgroups|complete|compquote|comptags|comptry|compvalues| \ | |
declare|dirs|disable|disown|dosh|echotc|echoti|help|history| \ | |
hist|let|local|login|logout|map|mapfile|popd|print|pushd| \ | |
readarray|repeat|savehistory|source|shopt|stop|suspend|typeset|whence) | |
command \return 1;; | |
esac; | |
}; | |
set_type () { | |
case "$(command \type "${1:-_}" 2>/dev/null)" in | |
(*\ function) | |
command \echo function;; | |
(*\ builtin) | |
command \echo built-in;; | |
(*\ keyword) | |
command \echo keyword;; | |
(*\ not\ found|"") | |
command \echo unkown;; | |
(*) | |
command \echo file;; | |
esac; | |
}; | |
test_name () | |
case "$1" in | |
(admin|ar|asa|at|awk|basename|batch|bc|c99|cal|cat|cflow|chgrp| \ | |
chmod|chown|cksum|cmp|comm|compress|cp|crontab|csplit|ctags|cut| \ | |
cxref|date|dd|delta|df|diff|dirname|du|echo|ed|env|ex|expand| \ | |
expr|file|find|fold|fort77|fuser|gencat|get|getconf|grep|head| \ | |
iconv|id|ipcrm|ipcs|join|lex|link|ln|locale|localedef|logger| \ | |
logname|lp|ls|m4|mailx|make|man|mesg|mkdir|mkfifo|more|mv|nice| \ | |
nl|nm|nohup|od|paste|patch|pathchk|pax|pr|printf|prs|ps|qalter| \ | |
qdel|qhold|qmove|qmsg|qrerun|qrls|qselect|qsig|qstat|qsub|renice| \ | |
rm| rmdel|rmdir|sact|sccs|sed|sh|sleep|sort|split|strings|strip| \ | |
stty|tabs|tail|talk|tee|test|time|touch|tput|tr|tsort|tty|uname| \ | |
uncompress|unexpand|unget|uniq|unlink|uucp|uudecode|uuencode|uustat| \ | |
uux|val|vi|wc|what|who|write|xargs|yacc|zcat) | |
command \echo 1;; | |
(alias|bg|cd|command|false|fc|fg|getopts|hash|jobs|kill|newgrp|pwd|read| \ | |
true|type|ulimit|umask|unalias|wait) | |
command \echo 2;; | |
esac; | |
iam () { | |
command \local -; | |
command \set -- ${1}; | |
command \unalias "$1" 2>/dev/null; | |
command \local cmd=; | |
cmd="$1"; | |
command \printf '%s\n' "Using token '${cmd}'"; | |
command \printf '%s' "Testing if token matches operator ... "; | |
'has_operator' "$cmd"; | |
case "$?" in | |
(0) | |
command \printf '%s\n' "False";; | |
(1) | |
command \printf '%s\n' "True"; | |
command \printf '\t%s\n' "Token matches control operator"; | |
command \printf '\t%s\n' "Skipping"; | |
command \return 0;; | |
(2) | |
command \printf '%s\n' "True"; | |
command \printf '\t%s\n' "Token matches redirection operator"; | |
command \printf '\t%s\n' "Skipping"; | |
command \return 0;; | |
esac; | |
command \printf '%s' "Testing if token is reserved word ... "; | |
'is_reserved_word' "$cmd"; | |
case "$?" in | |
(0) | |
command \printf '%s\n' "False";; | |
(1) | |
command \printf '%s\n' "True"; | |
command \printf '\t%s\n' "Skipping"; | |
command \return 0;; | |
(2) | |
command \printf '%s\n' "True"; | |
command \printf '\t%s\n' "Token is unspecific reserved word"; | |
command \printf '\t%s\n' "Skipping"; | |
command \return 0;; | |
esac; | |
command \printf '%s\n' "Starting command search and execution"; | |
command \printf '%s' "Testing if name has slashes ... "; | |
'has_slash' "$cmd" || { | |
command \printf '%s\n' "True"; | |
command \printf '\t%s' "Searching name using the PATH environment variable ... "; | |
command \local p=; | |
p="$('get_path' "$cmd")"; | |
if | |
command \test -n "$p"; | |
then | |
command \printf '%s\n' "Match"; | |
case "$('test_name' "$(command \basename "$p")")" in | |
(1) | |
command \printf '\t\t%s\n' "Name is a POSIX standard utility";; | |
esac; | |
command \printf '\t\t%s\n' "Name points to file '${p}'"; | |
command \printf '\t\t%s\n' "Executing file '${p}'"; | |
command \return 0; | |
else | |
command \printf '%s\n' "Unmatch"; | |
command \printf '\t%s\n' "Skipping command search and execution of '${cmd}'"; | |
command \return 127; | |
fi; | |
}; | |
command \printf '%s\n' "False"; | |
command \printf '%s' "Testing if name is POSIX special built-in ... "; | |
if | |
'is_special_builtin' "$cmd"; | |
then | |
command \printf '%s\n' "False"; | |
else | |
command \printf '%s\n' "True"; | |
command \printf '\t%s\n' "Executing special built-in '${cmd}'"; | |
command \return 0; | |
fi; | |
command \printf '%s' "Testing if name is POSIX-unspecific utility ... "; | |
if | |
'is_unspecified_utility' "$cmd"; | |
then | |
command \printf '%s\n' "False"; | |
else | |
command \printf '%s\n' "True"; | |
command \printf '\t%s\n' "Skipping command search and execution of '${cmd}'"; | |
command \return 0; | |
fi; | |
command \local t=; | |
t="$('set_type' "$cmd")"; | |
command \printf '%s' "Testing if name is definied function ... "; | |
if | |
'is_function' "$t"; | |
then | |
command \printf '%s\n' "True"; | |
command \printf '\t%s' "Testing if function is named as POSIX standard utility ... "; | |
case "$('test_name' "$cmd")" in | |
(1) | |
command \printf '%s\n' "True"; | |
command \printf '\t\t%s\n' "Postponing execution of '${cmd}'";; | |
(2) | |
command \printf '%s\n' "True"; | |
command \printf '\t\t%s\n' \ | |
"Function is named as POSIX regular built-in" \ | |
"Executing function '${cmd}'"; | |
command \return 0;; | |
(*) | |
command \printf '%s\n' "False"; | |
command \printf '\t%s\n' "Executing function '${cmd}'"; | |
command \return 0;; | |
esac; | |
else | |
command \printf '%s\n' "False"; | |
fi; | |
command \printf '%s' "Testing if name is built-in ... "; | |
if | |
'is_builtin' "$t"; | |
then | |
command \printf '%s\n' "True"; | |
command \printf '\t%s' "Testing if built-in is a POSIX regular built-in ... "; | |
case "$('test_name' "$cmd")" in | |
(1) | |
command \printf '%s\n' "False"; | |
command \printf '\t%s\n' "Built-in is regular for this shell, but only a POSIX standard utility"; | |
command \printf '\t%s\n' "Postponing execution of ${cmd}";; | |
(2) | |
command \printf '%s\n' "True"; | |
command \printf '\t\t%s\n' \ | |
"Executing POSIX regluar built-in '${cmd}'"; | |
command \return 0;; | |
(*) | |
command \printf '%s\n' "False"; | |
command \printf '\t%s\n' "Unknown POSIX non-standard built-in"; | |
command \printf '\t%s\n' "Skipping command search and execution of '${cmd}'"; | |
command \return 0;; | |
esac; | |
else | |
command \printf '%s\n' "False"; | |
fi; | |
command \printf '%s' "Searching name using the PATH environment variable ... "; | |
command \local p=; | |
p="$('get_path' "$cmd")"; | |
if | |
command \test -n "$p"; | |
then | |
command \printf '%s\n' "Match"; | |
case "$t" in | |
(built-in|function) | |
command \printf '\t%s\n' "Executing postponed ${t} '${cmd}'"; | |
command \return 0;; | |
(file) | |
case "$('test_name' "$cmd")" in | |
(1) | |
command \printf '\t%s\n' "Name is a POSIX standard utility"; | |
esac; | |
command \printf '\t%s\n' "Name points to file '${p}'"; | |
command \printf '\t%s\n' "Executing file '${p}'";; | |
esac; | |
else | |
command \printf '%s\n' "Unmatch"; | |
command \printf '%s\n' "Skipping command search and execution of '${cmd}'"; | |
command \return 127; | |
fi; | |
}; | |
# -- MAIN. | |
'iam' "${1:?}" 1>&2; | |
# vim: set ts=4 sw=4 tw=0 et : |
Author
d630
commented
Nov 26, 2016
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment