GNU/Linux >> Linux Esercitazione >  >> Linux

Differenze sub-shell tra bash e ksh

In ksh, una subshell potrebbe o meno risultare in un nuovo processo. Non so quali siano le condizioni, ma la shell è stata ottimizzata per le prestazioni su sistemi in cui fork() era più costoso di quanto non sia in genere su Linux, quindi evita di creare un nuovo processo ogni volta che può. La specifica dice un "nuovo ambiente", ma quella separazione ambientale può essere fatta durante il processo.

Un'altra differenza vagamente correlata è l'utilizzo di nuove lavorazioni per i tubi. In ksh e zsh, se l'ultimo comando in una pipeline è incorporato, viene eseguito nel processo shell corrente, quindi funziona:

$ unset x
$ echo foo | read x
$ echo $x
foo
$

In bash, tutti i comandi della pipeline dopo il primo vengono eseguiti in subshell, quindi quanto sopra non funziona:

$ unset x
$ echo foo | read x
$ echo $x

$

Come sottolinea @dave-thompson-085, puoi ottenere il comportamento ksh/zsh nelle versioni bash 4.2 e successive se disattivi il controllo del lavoro (set +o monitor ) e accendi il lastpipe opzione (shopt -s lastpipe ). Ma la mia solita soluzione è usare invece la sostituzione del processo:

$ unset x
$ read x < <(echo foo)
$ echo $x
foo

ksh93 lavora in modo insolitamente duro per evitare le subshell. Parte del motivo è l'evitamento di stdio e l'uso estensivo di sfio che consente ai builtin di comunicare direttamente. Un altro motivo è che ksh può in teoria avere così tanti built-in. Se compilato con SHOPT_CMDLIB_DIR , tutti i incorporati cmdlib sono inclusi e abilitati per impostazione predefinita. Non posso fornire un elenco completo di luoghi in cui le subshell vengono evitate, ma in genere è in situazioni in cui vengono utilizzati solo i built-in e dove non ci sono reindirizzamenti.

#!/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]"

fuori:

Version AJM 93v- 2013-02-22
31732 31732
31735 31732
31736 31732 
31732 31732 
31732 31732
31732 31732 
31732 31732
31738 31732

Un'altra netta conseguenza di tutta questa gestione interna dell'I/O è che alcuni problemi di buffering scompaiono. Ecco un divertente esempio di lettura di righe con tee e head builtins (non provarlo in nessun'altra 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

La manpage di bash riporta:

Ogni comando in una pipeline viene eseguito come un processo separato (ovvero, in una subshell).

Sebbene questa frase riguardi le pipe, implica fortemente che una subshell sia un processo separato.

La pagina di disambiguazione di Wikipedia descrive anche una subshell in termini di processo figlio. Un processo figlio è certamente esso stesso un processo.

La manpage ksh (a colpo d'occhio) non è diretta sulla propria definizione di subshell, quindi non implica in un modo o nell'altro che una subshell sia un processo diverso.

Imparare la conchiglia Korn dice che sono processi diversi.

Direi che ti manca qualcosa (o che il libro è sbagliato o obsoleto).


Linux
  1. Spiegazione delle differenze tra editor di testo Vi e Vim

  2. La differenza tra [[ $a ==Z* ]] e [ $a ==Z* ]?

  3. È consentito lo spazio tra #! E /bin/bash a Shebang?

  4. Quali sono le differenze tra `chattr +i FILE` e `chmod -w FILE`?

  5. Quali sono le differenze tra lsof e netstat su Linux?

Differenza tra la definizione di variabili Bash con e senza esportazione

Differenze tra AWSstats e Google Analytics

Differenza tra virgolette singole e doppie in Bash Shell

Vim vs Vi:somiglianze e differenze tra VIM e VI?

Differenze tra firewall hardware e software

Differenze tra nobootwait e nofail nei filesystem Linux