It is not the echo
behavior. It is a bash behavior. When you use echo $x
form the bash get the following command to process (treat ␣
as space):
echo␣␣hello
Then this command is tokenized and bash get two tokens: echo
and hello
thus the output is just hello
When you use the echo "$x"
form then the bash has the following at the input of tokenizer:
echo␣"␣hello"
thus it has two tokens echo
and ␣hello
, so the output is different.
The zombie isn't waiting for its child. Like any zombie process, it stays around until its parent collects it.
You should display all the processes involved to understand what's going on, and look at the PPID as well. Use this command line:
ps -t $(tty) -O ppid,pgid
The parent of the process you're killing is cat
. What happens is that bash runs the background command cat <( sleep 100 & wait )
in a subshell. Since the only thing this subshell does is to set up some redirection and then run an external command, this subshell is replaced by the external command. Here's the rundown:
- The original bash (12126) calls
fork
to execute the background command cat <( sleep 100 & wait )
in a child (14247).
- The child (14247) calls
pipe
to create a pipe, then fork
to create a child to run the process substitution sleep 100 & wait
.
- The grandchild (14248) calls
fork
to run sleep 100
in the background. Since the grandchild isn't interactive, the background process doesn't run in a separate process group. Then the grandchild waits for sleep
to exit.
- The child (14247) calls
setpgid
(it's a background job in an interactive shell so it gets its own process group), then execve
to run cat
. (I'm a bit surprised that the process substitution isn't happening in the background process group.)
- You kill the grandchild (14248). Its parent is running
cat
, which knows nothing about any child process and has no business calling wait
. Since the grandchild's parent doesn't reap it, the grandchild stays behind as a zombie.
- Eventually,
cat
exits — either because you kill it, or because sleep
returns and closes the pipe so cat
sees the end of its input. At that point, the zombie's parent dies, so the zombie is collected by init and init reaps it.
If you change the command to
{ cat <( sleep 100 & wait ); echo done; } &
then cat
runs in a separate process, not in the child of the original bash process: the first child has to stay behind to run echo done
. In this case, if you kill the grandchild, it doesn't stay on as a zombie, because the child (which is still running bash at that point) reaps it.
See also How does linux handles zombie process and Can a zombie have orphans? Will the orphan children be disturbed by reaping the zombie?
Best Answer
Yes, this is using history.
!#
is a history event designator that refers to the entire command line typed so far.:*
is a word (range) designator that refers to all of the words, except the 0th. So, after you have typedecho "This is a sentence. "
, then!#:*
expands to"This is a sentence. "
. Andx-y
(where x and y are integers) is a word (range) designator that refers to word number x through word number y. If y is omitted (x-
), this is interpreted to mean word number x through the second to last word. So, after your “entire command line typed so far” stands asthen
!#:1-
expands to"This is a sentence. "
, because each of the quoted"This is a sentence. "
strings counts as one word, and so!#:1-
is equivalent to!#:1
(just word number 1). So you end up withThe fact that the
-
and the>
appear together in the command is just a confusion; they don’t interact. And the fact that “This is a sentence.” is quoted obscures what is going on; if you saidit would expand to
and thence to
(because
!#:1-
expands to word number 1 through the second to last word.)