Skip to main content
added 904 characters in body
Source Link
Stéphane Chazelas
  • 584.8k
  • 96
  • 1.1k
  • 1.7k

If you do for instance:

 strace ksh93 -c 'pwd; (cd /; umask 0; pwd; exit 2); pwd'

You'll see ksh93 not forking any process but do something like this instead:

openat(AT_FDCWD, ".", O_RDONLY|O_PATH)  = 3
fcntl(3, F_DUPFD, 10)                   = 10
close(3)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
[...]

Which saves the current directory on fd 10. Then:

chdir("/")                              = 0
umask(000)                              = 002

Which changes the current directory and umask in the subshell. And upon termination of the subshell (the exit 2 not calling the _exit() system call):

fchdir(10)                              = 0
close(10)                               = 0

To restore the current working directory and:

umask(002)                              = 000

To restore the umask.

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

If you do for instance:

 strace ksh93 -c 'pwd; (cd /; umask 0; pwd; exit 2); pwd'

You'll see ksh93 not forking any process but do something like this instead:

openat(AT_FDCWD, ".", O_RDONLY|O_PATH)  = 3
fcntl(3, F_DUPFD, 10)                   = 10
close(3)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
[...]

Which saves the current directory on fd 10. Then:

chdir("/")                              = 0
umask(000)                              = 002

Which changes the current directory and umask in the subshell. And upon termination of the subshell (the exit 2 not calling the _exit() system call):

fchdir(10)                              = 0
close(10)                               = 0

To restore the current working directory and:

umask(002)                              = 000

To restore the umask.

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

edited body
Source Link
Stéphane Chazelas
  • 584.8k
  • 96
  • 1.1k
  • 1.7k

In the bash shell, subshells are implemented by forking a child process, so you won't see a case of a subshell not running in a child process in that shell.

ksh93 is the only shell that I know that skips the forking when possible for subshells (an optimisation that is still quite buggy and that the successive people that have tried to maintain it after AT&T disbanded the team that had written it have considered removing).

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

var=$(printf %04d "$n")

(here with a printf builtin, and no change to the environment is being done in there).

In a pipeline, all components have to run concurrently, so they have to run in separate processes, even in ksh93.

In bash, they all run in child processes. In AT&T ksh or zsh, or with bash -O lastpipe (when non-interactive), the rightmost one doesn't (of course, you still need to fork a child process to run external commands such as ps).

You don't see an extra bash process in ps >&2 | ps or (ps) because ps is executed directly in that child process, which before executing ps was bash interpreting the pipeline component: the subshell. For instance, in:

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$$""$n"

You'll see 2 and 0 in bash, and 2 and 2 in zsh/ksh93. /bin/true and /bin/echo are executed in child processes, /bin/true directly in the subshell process that had done n=1 earlier, same in bash for /bin/echo (and n=2), but in zsh/ksh/bash -O lastpipe, the n=2 was done in the main shell process, and a child only forked to execute that external utility, just like when you run /bin/echo "$((n=2))" not as part of a pipeline.

In bash (contrary to zsh/ksh), you do see an extra bash process in (: anything; ps), the optimisation is only done if the subshell has only one external command, you'd need to use exec to do that optimisation by hand there: (: anything; exec ps).

Same goes for { ps; } | cat.

In the bash shell, subshells are implemented by forking a child process, so you won't see a case of a subshell not running in a child process in that shell.

ksh93 is the only shell that I know that skips the forking when possible for subshells (an optimisation that is still quite buggy and that the successive people that have tried to maintain it after AT&T disbanded the team that had written it have considered removing).

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

var=$(printf %04d "$n")

(here with a printf builtin, and no change to the environment is being done in there).

In a pipeline, all components have to run concurrently, so they have to run in separate processes, even in ksh93.

In bash, they all run in child processes. In AT&T ksh or zsh, or with bash -O lastpipe (when non-interactive), the rightmost one doesn't (of course, you still need to fork a child process to run external commands such as ps).

You don't see an extra bash process in ps >&2 | ps or (ps) because ps is executed directly in that child process, which before executing ps was bash interpreting the pipeline component: the subshell. For instance, in:

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$$"

You'll see 2 and 0 in bash, and 2 and 2 in zsh/ksh93. /bin/true and /bin/echo are executed in child processes, /bin/true directly in the subshell process that had done n=1 earlier, same in bash for /bin/echo (and n=2), but in zsh/ksh/bash -O lastpipe, the n=2 was done in the main shell process, and a child only forked to execute that external utility, just like when you run /bin/echo "$((n=2))" not as part of a pipeline.

In bash (contrary to zsh/ksh), you do see an extra bash process in (: anything; ps), the optimisation is only done if the subshell has only one external command, you'd need to use exec to do that optimisation by hand there: (: anything; exec ps).

Same goes for { ps; } | cat.

In the bash shell, subshells are implemented by forking a child process, so you won't see a case of a subshell not running in a child process in that shell.

ksh93 is the only shell that I know that skips the forking when possible for subshells (an optimisation that is still quite buggy and that the successive people that have tried to maintain it after AT&T disbanded the team that had written it have considered removing).

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

var=$(printf %04d "$n")

(here with a printf builtin, and no change to the environment is being done in there).

In a pipeline, all components have to run concurrently, so they have to run in separate processes, even in ksh93.

In bash, they all run in child processes. In AT&T ksh or zsh, or with bash -O lastpipe (when non-interactive), the rightmost one doesn't (of course, you still need to fork a child process to run external commands such as ps).

You don't see an extra bash process in ps >&2 | ps or (ps) because ps is executed directly in that child process, which before executing ps was bash interpreting the pipeline component: the subshell. For instance, in:

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$n"

You'll see 2 and 0 in bash, and 2 and 2 in zsh/ksh93. /bin/true and /bin/echo are executed in child processes, /bin/true directly in the subshell process that had done n=1 earlier, same in bash for /bin/echo (and n=2), but in zsh/ksh/bash -O lastpipe, the n=2 was done in the main shell process, and a child only forked to execute that external utility, just like when you run /bin/echo "$((n=2))" not as part of a pipeline.

In bash (contrary to zsh/ksh), you do see an extra bash process in (: anything; ps), the optimisation is only done if the subshell has only one external command, you'd need to use exec to do that optimisation by hand there: (: anything; exec ps).

Same goes for { ps; } | cat.

deleted 4 characters in body
Source Link
Stéphane Chazelas
  • 584.8k
  • 96
  • 1.1k
  • 1.7k

In the bash shell, subshells are implemented by forking a child process, so you won't see a case of a subshell not running in a child process in that shell.

ksh93 is the only shell that I know that skips the forking when possible for subshells (an optimisation that is still quite buggy and that the successive people that have tried to maintain it after AT&T disbanded the team that had written it have considered removing).

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

var=$(printf %04d "$n")

(here with a printf builtin, and no change to the environment is being done in there).

In a pipeline, all components have to run concurrently, so they have to run in separate processes, even in ksh93.

In bash, they all run in child processes. In AT&T ksh or zsh, or with bash -O lastpipe (when non-interactive), the rightmost one doesn't (of course, you still need to fork a child process to run external commands such as ps).

You don't see an extra bash process in ps >&2 | ps or (ps) because ps is executed directly in that child process (which, which before executing ps was bash interpreting the pipeline component: the subshell. For instance, in:

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$$"

You'll see 2 and 0 in bash, and 2 and 2 in zsh/ksh93. /bin/true and /bin/echo are executed in child processes, /bin/true directly in the subshell process that had done n=1 earlier, same in bash for /bin/echo (and n=2), but in zsh/ksh/bash -O lastpipe, the n=2 was done in the main shell process, and a child only forked to execute that external utility, just like when you run /bin/echo "$((n=2))" not as part of a pipeline.

In bash (contrary to zsh/ksh), you do see an extra bash process in (: anything; ps), the optimisation is only done if the subshell has only one external command, you'd need to use exec to do that optimisation by hand there: (: anything; exec ps).

Same goes for { ps; } | cat.

In the bash shell, subshells are implemented by forking a child process, so you won't see a case of a subshell not running in a child process in that shell.

ksh93 is the only shell that I know that skips the forking when possible for subshells (an optimisation that is still quite buggy and that the successive people that have tried to maintain it after AT&T disbanded the team that had written it have considered removing).

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

var=$(printf %04d "$n")

(here with a printf builtin, and no change to the environment is being done in there).

In a pipeline, all components have to run concurrently, so they have to run in separate processes, even in ksh93.

In bash, they all run in child processes. In AT&T ksh or zsh, or with bash -O lastpipe (when non-interactive), the rightmost one doesn't (of course, you still need to fork a child process to run external commands such as ps).

You don't see an extra bash process in ps >&2 | ps or (ps) because ps is executed directly in that child process (which before executing ps was bash interpreting the pipeline component: the subshell).

In bash (contrary to zsh/ksh), you do see an extra bash process in (: anything; ps), the optimisation is only done if the subshell has only one external command, you'd need to use exec to do that optimisation by hand there: (: anything; exec ps).

Same goes for { ps; } | cat.

In the bash shell, subshells are implemented by forking a child process, so you won't see a case of a subshell not running in a child process in that shell.

ksh93 is the only shell that I know that skips the forking when possible for subshells (an optimisation that is still quite buggy and that the successive people that have tried to maintain it after AT&T disbanded the team that had written it have considered removing).

Some shells like FreeBSD's sh can skip the fork in very specific cases, like in:

var=$(printf %04d "$n")

(here with a printf builtin, and no change to the environment is being done in there).

In a pipeline, all components have to run concurrently, so they have to run in separate processes, even in ksh93.

In bash, they all run in child processes. In AT&T ksh or zsh, or with bash -O lastpipe (when non-interactive), the rightmost one doesn't (of course, you still need to fork a child process to run external commands such as ps).

You don't see an extra bash process in ps >&2 | ps or (ps) because ps is executed directly in that child process, which before executing ps was bash interpreting the pipeline component: the subshell. For instance, in:

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$$"

You'll see 2 and 0 in bash, and 2 and 2 in zsh/ksh93. /bin/true and /bin/echo are executed in child processes, /bin/true directly in the subshell process that had done n=1 earlier, same in bash for /bin/echo (and n=2), but in zsh/ksh/bash -O lastpipe, the n=2 was done in the main shell process, and a child only forked to execute that external utility, just like when you run /bin/echo "$((n=2))" not as part of a pipeline.

In bash (contrary to zsh/ksh), you do see an extra bash process in (: anything; ps), the optimisation is only done if the subshell has only one external command, you'd need to use exec to do that optimisation by hand there: (: anything; exec ps).

Same goes for { ps; } | cat.

deleted 4 characters in body
Source Link
Stéphane Chazelas
  • 584.8k
  • 96
  • 1.1k
  • 1.7k
Loading
added 188 characters in body
Source Link
Stéphane Chazelas
  • 584.8k
  • 96
  • 1.1k
  • 1.7k
Loading
Source Link
Stéphane Chazelas
  • 584.8k
  • 96
  • 1.1k
  • 1.7k
Loading