GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Sub-Shell-Unterschiede zwischen bash und ksh

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).


Linux
  1. Unterschiede zwischen Vi- und Vim-Texteditoren erklärt

  2. Was ist der Unterschied zwischen #!/usr/bin/env bash und #!/usr/bin/bash?

  3. Unterschied zwischen ${} und $() in Bash

  4. Was sind die Unterschiede zwischen lsof und netstat unter Linux?

  5. Was sind die Unterschiede zwischen rdesktop und xfreerdp?

Unterschied zwischen der Definition von Bash-Variablen mit und ohne Export

Unterschiede zwischen AWStats und Google Analytics

Unterschied zwischen einfachen und doppelten Anführungszeichen in Bash Shell

Vim vs. Vi – Ähnlichkeiten und Unterschiede zwischen VIM und VI?

Unterschiede zwischen nobootwait und nofail in Linux-Dateisystemen

Unterschied zwischen Befehlen im Bash-Skript und Befehlen im Terminal