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