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