GNU/Linux >> Tutoriales Linux >  >> Linux

Diferencias de subcapa entre bash y ksh

En ksh, una subcapa puede o no dar como resultado un nuevo proceso. No sé cuáles son las condiciones, pero el shell se optimizó para el rendimiento en sistemas donde fork() era más caro de lo que suele ser en Linux, por lo que evita crear un nuevo proceso siempre que puede. La especificación dice un "nuevo entorno", pero esa separación ambiental puede realizarse en el proceso.

Otra diferencia vagamente relacionada es el uso de nuevos procesos para tuberías. En ksh y zsh, si el último comando en una canalización es un comando integrado, se ejecuta en el proceso de shell actual, así que esto funciona:

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

En bash, todos los comandos de canalización después del primero se ejecutan en subcapas, por lo que lo anterior no funciona:

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

$

Como señala @ dave-thompson-085, puede obtener el comportamiento ksh/zsh en las versiones 4.2 y posteriores de bash si desactiva el control de trabajos (set +o monitor ) y enciende el lastpipe opción (shopt -s lastpipe ). Pero mi solución habitual es usar la sustitución de procesos en su lugar:

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

ksh93 trabaja inusualmente duro para evitar subcapas. Parte de la razón es la evitación de stdio y el uso extensivo de sfio que permite que los componentes integrados se comuniquen directamente. Otra razón es que, en teoría, ksh puede tener tantas funciones integradas. Si se construye con SHOPT_CMDLIB_DIR , todas las funciones integradas de cmdlib están incluidas y habilitadas de forma predeterminada. No puedo dar una lista completa de los lugares donde se evitan las subcapas, pero normalmente es en situaciones en las que solo se usan componentes integrados y donde no hay redireccionamientos.

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

fuera:

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

Otra clara consecuencia de todo este manejo interno de E/S es que algunos problemas de almacenamiento en búfer simplemente desaparecen. Aquí hay un ejemplo divertido de leer líneas con tee y head incorporados (no intente esto en ningún otro 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 página de manual de bash dice:

Cada comando en una canalización se ejecuta como un proceso separado (es decir, en una subcapa).

Si bien esta oración se trata de tuberías, implica fuertemente que una subcapa es un proceso separado.

La página de desambiguación de Wikipedia también describe una subcapa en términos de procesos secundarios. Un proceso hijo es ciertamente un proceso en sí mismo.

La página de manual de ksh (de un vistazo) no es directa sobre su propia definición de subcapa, por lo que no implica de una forma u otra que una subcapa sea un proceso diferente.

Aprender el Korn Shell dice que son procesos diferentes.

Yo diría que te estás perdiendo algo (o que el libro está mal o desactualizado).


Linux
  1. Explicación de las diferencias entre los editores de texto Vi y Vim

  2. Diferencia entre ${} y $() en Bash

  3. ¿Cuáles son las diferencias entre lsof y netstat en Linux?

  4. Diferencia entre comandos en bash script y comandos en terminal

  5. ¿Cuáles son las diferencias entre rdesktop y xfreerdp?

Diferencia entre la definición de variables Bash con y sin exportación

Diferencias entre AWStats y Google Analytics

Diferencia entre comillas simples y dobles en Bash Shell

Vim vs Vi:¿similitudes y diferencias entre VIM y VI?

Diferencias entre nobootwait y nofail en los sistemas de archivos de Linux

¿Cuál es la diferencia entre &> y >&en bash?