GNU/Linux >> Tutoriels Linux >  >> Linux

Différences de sous-shell entre bash et ksh

Dans ksh, un sous-shell peut ou non entraîner un nouveau processus. Je ne sais pas quelles sont les conditions, mais le shell a été optimisé pour les performances sur les systèmes où fork() était plus cher qu'il ne l'est généralement sous Linux, il évite donc de créer un nouveau processus chaque fois que cela est possible. La spécification parle d'un "nouvel environnement", mais cette séparation environnementale peut être effectuée en cours de processus.

Une autre différence vaguement liée est l'utilisation de nouveaux procédés pour les tuyaux. Dans ksh et zsh, si la dernière commande d'un pipeline est une commande intégrée, elle s'exécute dans le processus shell actuel, donc cela fonctionne :

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

Dans bash, toutes les commandes de pipeline après la première sont exécutées dans des sous-shells, donc ce qui précède ne fonctionne pas :

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

$

Comme le souligne @dave-thompson-085, vous pouvez obtenir le comportement ksh/zsh dans les versions 4.2 et ultérieures de bash si vous désactivez le contrôle des tâches (set +o monitor ) et activez le lastpipe choix (shopt -s lastpipe ). Mais ma solution habituelle consiste à utiliser la substitution de processus à la place :

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

ksh93 travaille exceptionnellement dur pour éviter les sous-shells. Une partie de la raison est l'évitement de stdio et l'utilisation intensive de sfio qui permet aux commandes intégrées de communiquer directement. Une autre raison est que ksh peut en théorie avoir autant de fonctions intégrées. Si construit avec SHOPT_CMDLIB_DIR , toutes les commandes intégrées cmdlib sont incluses et activées par défaut. Je ne peux pas donner une liste complète des endroits où les sous-shells sont évités, mais c'est généralement dans des situations où seuls les builtins sont utilisés et où il n'y a pas de redirections.

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

sortie :

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

Une autre conséquence intéressante de toute cette gestion interne des E/S est que certains problèmes de mise en mémoire tampon disparaissent. Voici un exemple amusant de lecture de lignes avec tee et head builtins (n'essayez pas cela dans un autre 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 page de manuel bash indique :

Chaque commande d'un pipeline est exécutée en tant que processus distinct (c'est-à-dire dans un sous-shell).

Bien que cette phrase concerne les canaux, elle implique fortement qu'un sous-shell est un processus distinct.

La page d'homonymie de Wikipedia décrit également un sous-shell en termes de processus enfant. Un processus enfant est certainement lui-même un processus.

La page de manuel de ksh (en un coup d'œil) n'est pas directe sur sa propre définition d'un sous-shell, elle n'implique donc pas d'une manière ou d'une autre qu'un sous-shell est un processus différent.

Apprendre le Korn Shell dit qu'il s'agit de processus différents.

Je dirais qu'il vous manque quelque chose (ou que le livre est erroné ou obsolète).


Linux
  1. Différence entre " et " sur la ligne de commande (bash) ? ?

  2. Différences entre volume, partition et lecteur ?

  3. Quelle est la différence entre #!/usr/bin/env bash et #!/usr/bin/bash ?

  4. Quelles sont les différences entre lsof et netstat sous Linux ?

  5. Différence entre les commandes dans le script bash et les commandes dans le terminal

Différence entre la définition de variables bash avec et sans exportation

Différences entre AWStats et Google Analytics

Différence entre les guillemets simples et doubles dans Bash Shell

Vim vs Vi - Similitudes et différences entre VIM et VI ?

Différences entre les pare-feu matériels et logiciels

Différences entre nobootwait et nofail dans les systèmes de fichiers Linux