GNU/Linux >> Belajar Linux >  >> Linux

Perbedaan sub-kulit antara bash dan ksh

Di ksh, subkulit mungkin atau mungkin tidak menghasilkan proses baru. Saya tidak tahu apa kondisinya, tetapi shell dioptimalkan untuk kinerja pada sistem di mana fork() lebih mahal daripada biasanya di Linux, jadi menghindari pembuatan proses baru kapan pun bisa. Spesifikasi mengatakan "lingkungan baru", tetapi pemisahan lingkungan itu dapat dilakukan dalam proses.

Perbedaan samar terkait lainnya adalah penggunaan proses baru untuk pipa. Di ksh dan zsh, jika perintah terakhir dalam pipa adalah builtin, itu berjalan dalam proses shell saat ini, jadi ini berfungsi:

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

Di bash, semua perintah pipa setelah yang pertama dijalankan dalam subkulit, jadi hal di atas tidak berfungsi:

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

$

Seperti yang ditunjukkan oleh @dave-thompson-085, Anda bisa mendapatkan perilaku ksh/zsh di bash versi 4.2 dan yang lebih baru jika Anda mematikan kontrol pekerjaan (set +o monitor ) dan aktifkan lastpipe opsi (shopt -s lastpipe ). Tapi solusi saya yang biasa adalah menggunakan substitusi proses:

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

ksh93 bekerja sangat keras untuk menghindari subkulit. Sebagian alasannya adalah menghindari stdio dan penggunaan ekstensif sfio yang memungkinkan builtin untuk berkomunikasi secara langsung. Alasan lain adalah ksh secara teori dapat memiliki begitu banyak builtin. Jika dibangun dengan SHOPT_CMDLIB_DIR , semua bawaan cmdlib disertakan dan diaktifkan secara default. Saya tidak dapat memberikan daftar lengkap tempat-tempat di mana subkulit dihindari, tetapi biasanya dalam situasi di mana hanya bawaan yang digunakan, dan di mana tidak ada pengalihan.

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

keluar:

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

Konsekuensi lain dari semua penanganan I/O internal ini adalah beberapa masalah buffering hilang begitu saja. Inilah contoh lucu membaca baris dengan tee dan head bawaan (jangan coba ini di shell lain).

 $ 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

Halaman manual bash berbunyi:

Setiap perintah dalam pipeline dijalankan sebagai proses terpisah (yaitu, dalam subkulit).

Meskipun kalimat ini tentang pipa, ini sangat menyiratkan bahwa subkulit adalah proses terpisah.

Halaman disambiguasi Wikipedia juga menjelaskan subkulit dalam istilah proses anak. Proses anak itu sendiri tentu saja merupakan proses.

Halaman manual ksh (sekilas) tidak langsung tentang definisi subkulitnya sendiri, jadi ini tidak menyiratkan bahwa subkulit adalah proses yang berbeda.

Mempelajari Cangkang Korn mengatakan bahwa mereka adalah proses yang berbeda.

Menurut saya, Anda melewatkan sesuatu (atau bukunya salah atau ketinggalan zaman).


Linux
  1. Perbedaan Antara Editor Teks Vi Dan Vim Dijelaskan

  2. Perbedaan Antara ' Dan ” Di Command Line (bash)??

  3. Perbedaan Antara Volume, Partisi, dan Drive?

  1. Perbedaan Antara Perangkat Keras dan Perangkat Lunak Firewall

  2. Perbedaan antara ${} dan $() di Bash

  3. Apa perbedaan antara &> dan >&di bash?

  1. Perbedaan Antara [[ $a ==Z* ]] Dan [ $a ==Z* ]?

  2. Apakah Ruang Diizinkan Antara #! Dan /bin/bash Di Shebang?

  3. Apa perbedaan antara rdesktop dan xfreerdp?