Let's assume the following script:
echo "if test 1 -eq $1 ; then echo ls;fi" | tee /tmp/toto
cat /tmp/toto
$(cat /tmp/toto)
While running it with this command:
./non_secure_script 1
I get the following error:
if: command not found
However, the following command run with success:
$(if test 1 -eq 1; then echo ls;fi)
Why is this happening?
CodePudding user response:
Because most parsing of traditional/standard (i.e. not *csh, rc, etc) shell input, in particular recognizing reserved words like if
and compound commands like if ...; then ...; fi
is done before expansions like $( command )
. See the sequence defined by POSIX particularly steps 2 and 3 and sections 2.2-2.4 later on the page, versus step 4 and sections 2.5-2.6 later on the page, not 'wordexp' as linked(!!!).
If you did $(/tmp/toto)
or $(. /tmp/toto)
-- or $(source /tmp/toto)
on shells that have that as a synonym -- it would execute the if-construct in the subshell and return to the main shell only the string ls
(or nothing). Since the main shell has parsed this as a simple-command (see section 2.9.1 on the page linked above) before expanding, and ls
is a valid simple-command, it is executed successfully. Because word-splitting and pathname-expansion (often called globbing) are done (unless disabled) as the last steps of expansion in section 2.6, returning a string with multiple space-separated words like ls -la
would work, unless they contain glob operators like ?
or *
, but quoting like ls -la 'file with spaces'
would not work.
Your second case $(if...fi)
works for the same reason; it executes the if-construct in the subshell and returns only ls
to be executed in the main shell, which is a valid simple-command.