In ksh kann eine Subshell zu einem neuen Prozess führen oder auch nicht. Ich weiß nicht, wie die Bedingungen sind, aber die Shell wurde für die Leistung auf Systemen optimiert, auf denen fork()
war teurer als es normalerweise unter Linux ist, daher vermeidet es die Erstellung eines neuen Prozesses, wann immer es möglich ist. Die Spezifikation sagt eine "neue Umgebung", aber diese Umgebungstrennung kann während des Prozesses erfolgen.
Ein weiterer vage verwandter Unterschied ist die Verwendung neuer Verfahren für Rohre. Wenn in ksh und zsh der letzte Befehl in einer Pipeline ein eingebauter Befehl ist, wird er im aktuellen Shell-Prozess ausgeführt, also funktioniert das:
$ unset x
$ echo foo | read x
$ echo $x
foo
$
In Bash werden alle Pipeline-Befehle nach dem ersten in Subshells ausgeführt, sodass das obige nicht funktioniert:
$ unset x
$ echo foo | read x
$ echo $x
$
Wie @dave-thompson-085 betont, können Sie das ksh/zsh-Verhalten in den Bash-Versionen 4.2 und höher erhalten, wenn Sie die Jobsteuerung deaktivieren (set +o monitor
) und schalten Sie lastpipe
ein Option (shopt -s lastpipe
). Aber meine übliche Lösung besteht darin, stattdessen die Prozesssubstitution zu verwenden:
$ unset x
$ read x < <(echo foo)
$ echo $x
foo
ksh93 arbeitet ungewöhnlich hart daran, Subshells zu vermeiden. Ein Grund dafür ist zum Teil die Vermeidung von stdio und die umfangreiche Verwendung von sfio, was es Einbauten ermöglicht, direkt zu kommunizieren. Ein weiterer Grund ist, dass ksh theoretisch so viele Builtins haben kann. Wenn mit SHOPT_CMDLIB_DIR
gebaut sind alle cmdlib-Builts enthalten und standardmäßig aktiviert. Ich kann keine umfassende Liste von Stellen geben, an denen Subshells vermieden werden, aber es ist typischerweise in Situationen, in denen nur eingebaute Elemente verwendet werden und in denen es keine Umleitungen gibt.
#!/usr/bin/env ksh
# doCompat arr
# "arr" is an indexed array name to be assigned an index corresponding to the detected shell.
# 0 = Bash, 1 = Ksh93, 2 = mksh
function doCompat {
${1:+:} return 1
if [[ ${BASH_VERSION+_} ]]; then
shopt -s lastpipe extglob
eval "${1}[0]="
else
case "${BASH_VERSINFO[*]-${!KSH_VERSION}}" in
.sh.version)
nameref v=$1
v[1]=
if builtin pids; then
function BASHPID.get { .sh.value=$(pids -f '%(pid)d'); }
elif [[ -r /proc/self/stat ]]; then
function BASHPID.get { read -r .sh.value _ </proc/self/stat; }
else
function BASHPID.get { .sh.value=$(exec sh -c 'echo $PPID'); }
fi 2>/dev/null
;;
KSH_VERSION)
nameref "_${1}=$1"
eval "_${1}[2]="
;&
*)
if [[ ! ${BASHPID+_} ]]; then
echo 'BASHPID requires Bash, ksh93, or mksh >= R41' >&2
return 1
fi
esac
fi
}
function main {
typeset -a myShell
doCompat myShell || exit 1 # stripped-down compat function.
typeset x
print -v .sh.version
x=$(print -nv BASHPID; print -nr " $$"); print -r "$x" # comsubs are free for builtins with no redirections
_=$({ print -nv BASHPID; print -r " $$"; } >&2) # but not with a redirect
_=$({ printf '%s ' "$BASHPID" $$; } >&2); echo # nor for expansions with a redirect
_=$(printf '%s ' "$BASHPID" $$ >&2); echo # but if expansions aren't redirected, they occur in the same process.
_=${ { print -nv BASHPID; print -r " $$"; } >&2; } # However, ${ ;} is always subshell-free (obviously).
( printf '%s ' "$BASHPID" $$ ); echo # Basically the same rules apply to ( )
read -r x _ <<<$(</proc/self/stat); print -r "$x $$" # These are free in {{m,}k,z}sh. Only Bash forks for this.
printf '%s ' "$BASHPID" $$ | cat # Sadly, pipes always fork. It isn't possible to precisely mimic "printf -v".
echo
} 2>&1
main "[email protected]"
aus:
Version AJM 93v- 2013-02-22
31732 31732
31735 31732
31736 31732
31732 31732
31732 31732
31732 31732
31732 31732
31738 31732
Eine weitere nette Folge dieser ganzen internen E/A-Verarbeitung ist, dass einige Pufferprobleme einfach verschwinden. Hier ist ein lustiges Beispiel für das Lesen von Zeilen mit tee
und head
builtins (versuchen Sie dies nicht in einer anderen Shell).
$ ksh -s <<\EOF
integer -a x
builtin head tee
printf %s\\n {1..10} |
while head -n 1 | [[ ${ { x+=("$(tee /dev/fd/{3,4})"); } 3>&1; } ]] 4>&1; do
print -r -- "${x[@]}"
done
EOF
1
0 1
2
0 1 2
3
0 1 2 3
4
0 1 2 3 4
5
0 1 2 3 4 5
6
0 1 2 3 4 5 6
7
0 1 2 3 4 5 6 7
8
0 1 2 3 4 5 6 7 8
9
0 1 2 3 4 5 6 7 8 9
10
0 1 2 3 4 5 6 7 8 9 10
Die Bash-Manpage lautet:
Jeder Befehl in einer Pipeline wird als separater Prozess (d. h. in einer Subshell) ausgeführt.
Dieser Satz handelt zwar von Pipes, impliziert aber stark, dass eine Subshell ein separater Prozess ist.
Die Disambiguierungsseite von Wikipedia beschreibt auch eine Subshell in Bezug auf untergeordnete Prozesse. Ein untergeordneter Prozess ist sicherlich selbst ein Prozess.
Die ksh-Manpage (auf einen Blick) ist nicht direkt in Bezug auf ihre eigene Definition einer Subshell, also impliziert sie nicht auf die eine oder andere Weise, dass eine Subshell ein anderer Prozess ist.
Erlernen der Korn-Shell sagt, dass es sich um unterschiedliche Prozesse handelt.
Ich würde sagen, Sie vermissen etwas (oder das Buch ist falsch oder veraltet).