The parentheses always start a subshell. What's happening is that bash detects that sleep 5
is the last command executed by that subshell, so it calls exec
instead of fork
+exec
. The sleep
command replaces the subshell in the same process.
In other words, the base case is:
( … )
create a subshell. The original process calls fork
and wait
. In the subprocess, which is a subshell:
sleep
is an external command which requires a subprocess of the subprocess. The subshell calls fork
and wait
. In the subsubprocess:
- The subsubprocess executes the external command →
exec
.
- Eventually the command terminates →
exit
.
wait
completes in the subshell.
wait
completes in the original process.
The optimization is:
( … )
create a subshell. The original process calls fork
and wait
. In the subprocess, which is a subshell until it calls exec
:
sleep
is an external command, and it's the last thing this process needs to do.
- The subprocess executes the external command →
exec
.
- Eventually the command terminates →
exit
.
wait
completes in the original process.
When you add something else after the call the sleep
, the subshell needs to be kept around, so this optimization can't happen.
When you add something else before the call to sleep
, the optimization could be made (and ksh does it), but bash doesn't do it (it's very conservative with this optimization).
No, the subshell was created first.
A shell execution environment contains shell parameters set by variable assignments and environment variables. A subshell environment was created by duplicating the shell environment, so it contains all the variables of the current shell environment.
See the example:
$ b=1
$ c=$(b=2; echo "$b")
$ echo "$c"
2
The output is 2
instead of 1
.
A subshell environment created by command substitution is different with a shell environment created by calling the shell executable.
When you call the shell as:
$ bash -c :
the the current shell used execve() to create new shell process, something like:
execve("/bin/bash", ["bash", "-c", ":"], [/* 64 vars */]) = 0
the last argument passed to execve
contains all the environment variables.
That's why you need to export the variables to push it to the environment variables, which will be included in subsequently executed commands:
$ a=; export a
$ strace -e execve bash -c :
execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0
+++ exited with 0 +++
Notice the environment variables change from 64 to 65. And variables which are not exported will not be passed to new shell environment:
$ a=; b=; export a
$ strace -e execve bash -c :
execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0
+++ exited with 0 +++
Notice the environment variables are still 65.
In command substitution, the shell used fork() to create new shell process, which just copied the current shell environment - which contains both variables set and environment variables.
Best Answer
In your case parenthesis
()
are used as array definition, for exampleIf you put single variable in array then you just have an array with single element.
To answer your other question whether parenthesis are also used for anything else: there are many situations in
bash
that in combination with other characters they can be used as:$()
<()
and>()
(command)
(())
fun () { echo x; }
?()
,*()
,+()
,@()
,!()
(only ifextglob
is enable)