GNU/Linux >> Znalost Linux >  >> Linux

Sub-shell rozdíly mezi bash a ksh

V ksh může nebo nemusí mít dílčí shell za následek nový proces. Nevím, jaké jsou podmínky, ale shell byl optimalizován pro výkon na systémech, kde fork() byl dražší než obvykle na Linuxu, takže se vyhýbá vytváření nového procesu, kdykoli to jde. Specifikace říká „nové prostředí“, ale toto oddělení prostředí může být provedeno během procesu.

Dalším nejasně souvisejícím rozdílem je použití nových procesů pro potrubí. Pokud je v ksh a zsh poslední příkaz v kanálu vestavěný, spustí se v aktuálním procesu shellu, takže to funguje:

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

V bash jsou všechny potrubní příkazy po prvním spuštěny v subshellech, takže výše uvedené nefunguje:

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

$

Jak zdůrazňuje @dave-thompson-085, chování ksh/zsh můžete získat v bash verze 4.2 a novější, pokud vypnete řízení úloh (set +o monitor ) a zapněte lastpipe možnost (shopt -s lastpipe ). Ale mým obvyklým řešením je místo toho použít substituci procesu:

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

ksh93 pracuje neobvykle tvrdě, aby se vyhnul podskořápkám. Částečným důvodem je vyhýbání se stdio a rozsáhlému používání sfio, které umožňuje vestavěným modulům komunikovat přímo. Dalším důvodem je, že ksh může mít teoreticky tolik vestavěných prvků. Pokud je vytvořen s SHOPT_CMDLIB_DIR , jsou ve výchozím nastavení zahrnuty a povoleny všechny vestavěné moduly cmdlib. Nemohu poskytnout úplný seznam míst, kde se subshellům vyhýbají, ale obvykle je to v situacích, kdy se používají pouze vestavěné moduly a kde nedochází k přesměrování.

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

ven:

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

Dalším pěkným důsledkem veškerého vnitřního zpracování I/O je, že některé problémy s vyrovnávací pamětí prostě zmizí. Zde je vtipný příklad čtení řádků s tee a head vestavěné (toto nezkoušejte v žádném jiném prostředí).

 $ 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

Manuová stránka bash zní:

Každý příkaz v kanálu se provádí jako samostatný proces (tj. v dílčím prostředí).

I když je tato věta o rourách, silně naznačuje, že podslupka je samostatný proces.

Zjednoznačňující stránka Wikipedie také popisuje podskořápku z hlediska podřízeného procesu. Podřízený proces je jistě sám o sobě procesem.

Manuálová stránka ksh (na první pohled) se přímo netýká vlastní definice podskořápky, takže z toho nevyplývá, že podskořápka je jiný proces.

Naučte se Korn Shell říká, že jde o různé procesy.

Řekl bych, že vám něco chybí (nebo je kniha špatná či zastaralá).


Linux
  1. Vysvětlení rozdílů mezi textovými editory Vi a Vim

  2. Rozdíl mezi ‚A‘ na příkazovém řádku (bash)??

  3. Je povoleno místo mezi #! A /bin/bash v Shebangu?

  1. Rozdíly mezi Sed na Mac Osx a dalšími „standardními“ Sedmi?

  2. Rozdíly mezi hardwarovými a softwarovými firewally

  3. Jaký je rozdíl mezi #!/usr/bin/env bash a #!/usr/bin/bash?

  1. Rozdíl mezi ${} a $() v Bash

  2. Jaký je rozdíl mezi `chattr +i FILE` a `chmod -w FILE`?

  3. Jaké jsou rozdíly mezi lsof a netstat na linuxu?