Search code examples
bashpathcommand-substitution

Command substitution and $PATH variable


Background

This [ article ] says :

The command substitution expands to the output of commands. These commands are executed in a subshell ..

But the bash manual says nothing about a subshell in its command substitution section.

My test below

$ ps
  PID TTY          TIME CMD
26483 pts/25   00:00:00 bash
26866 pts/25   00:00:00 ps
$ hpid="$(ps | grep bash)"
$ echo "$hpid"
26483 pts/25   00:00:00 bash
26899 pts/25   00:00:00 bash

shows that a new shell with pid 26899 was spawned during the command substitution. At this point I changed the PATH environment variable.

$ PATH="/some/rogue/path"

did the below stuff :

VAR="$(echo "Do|Die" | cut -d"|" -f 2)"

and got the below error :

Command 'cut' is available in '/usr/bin/cut'
The command could not be located because '/usr/bin' is not included in the PATH environment variable.
cut: command not found

I understand that the error is due to the modification of PATH environment variable which helps the shell locate the binaries. However I am confused when reading this together with command substitution.

If by $(..) a subshell is spawned, then PATH environment variable should be intact and should point to the binary (cut in this case) and so bash should not complain that it cannot locate the cut binary.

Question

How did the modification of the PATH affect the command substitution here?


Solution

  • Consider below example:

    $ export PS1='\$\$=$$ \$ '
    $$=30862 $ a=123 # Note: No export a here.
    $$=30862 $ echo $a
    123
    $$=30862 $ bash
    $$=31133 $ echo $a # Subshell explicitly created does not have it.
    
    $$=31133 $ exit
    $$=30862 $ echo $(eval 'echo $a') # This subshell however does inherit it. The single quote ensures that this is not evaluated by parent shell.
    123                               # echo $(echo $a) would probably cause $a to be evaluated by parent shell.
    $$=30862 $
    

    In short, subshells spawned by $(...) inherit same environment as parent shell, even if the variable is not exported. (Even $$ is same as parent shell.)