4

Put the following code into a shell script e.g. x.sh and chmod +x x.sh

./x.sh

what I expect is there is only one line, while actually, I got 2 lines:

tmp $ ./x.sh
52140 ttys003    0:00.00 /bin/sh ./x.sh
52142 ttys003    0:00.00 /bin/sh ./x.sh

My question is where does the 52142 come from?

#!/bin/sh
myself=$(basename $0)
running=$(ps -A | grep "$myself" |grep -v grep)
echo "${running}"

Note: this is on MacOS 10.12

Updated the question with new experiment:

#!/bin/sh
myself=$(basename $0)
running=$(ps -fe | grep $myself |grep -v grep)

echo ==== '$(ps -fe | grep "$myself" |grep -v grep)' ====
echo "${running}"
echo 

running=$(ps -fe | cat)
echo ==== '$(ps -fe | cat) |grep $myself' ====
echo "${running}"|grep $myself
echo 

running=$(ps -fe)
echo ==== '$(ps -fe) |grep $myself' ====
echo "${running}" |grep $myself

The output on MacOS 10.12 is:

==== $(ps -fe | grep "$myself" |grep -v grep) ====
  501 59912 81738   0  9:01AM ttys003    0:00.00 /bin/sh ./x.sh
  501 59914 59912   0  9:01AM ttys003    0:00.00 /bin/sh ./x.sh

==== $(ps -fe | cat) |grep $myself ====
  501 59912 81738   0  9:01AM ttys003    0:00.00 /bin/sh ./x.sh
  501 59918 59912   0  9:01AM ttys003    0:00.00 /bin/sh ./x.sh

==== $(ps -fe) |grep $myself ====
  501 59912 81738   0  9:01AM ttys003    0:00.00 /bin/sh ./x.sh

From above, it seems the subshell is also related to pipe.

Ren Ze
  • 41
  • 3

1 Answers1

4

$(command) is command substitution. The command was executed in a subshell.

A subshell is a child process (fork) of your original shell (or shell script), if you check with ps, the file name part is same as your original(parent) shell script.

You can verify it by changing your x.sh into:

echo "$(ps -fe --forest>foo.txt)"

With --forest ps will output a tree structure with sub processes. Open the foo.txt search x.sh, you will see the tree structure.

If I run here on my machine, I get:

kent     20866   707  0 15:55     \_ urxvt 
kent     20867 20866  0 15:55         \_ zsh
kent     21457 20867  0 15:56             \_ sh ./x.sh
kent     21459 21457  0 15:56                 \_ sh ./x.sh #subshell
kent     21460 21459  0 15:56                     \_ ps -fe --forest

If we add one more layer, change your script to:

(
echo "$(ps -fe --forest>foo.txt)"
)

Now our x.sh will create two nested subshells, if I run and check my foo.txt:

... 25882 27657  0 16:05 ...  \_ -zsh
... 31027 25882  0 16:16 ...      \_ sh ./x.sh
... 31028 31027  0 16:16 ...          \_ sh ./x.sh #subshell1
... 31029 31028  0 16:16 ...              \_ sh ./x.sh #subshell2
... 31031 31029  0 16:16 ...                  \_ ps -fe --forest

The $PPID show it too.

Kent
  • 189,393
  • 32
  • 233
  • 301
  • `ps T --forest` or `ps U $USER --forest`would make the search easier. – ULick May 02 '17 at 20:22
  • Great thanks Kent. The subshell listed seems also related to the pipe. Without the pipe in $(), the subshell doesn't shown. I couldn't attach code in comment and will update in the question. ps On MacOS, the --forest is not supported – Ren Ze May 03 '17 at 01:06