Home > database >  why does subshell returns if: command not found
why does subshell returns if: command not found

Time:12-17

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.

  • Related