bash和ksh之间的子shell差异

我一直认为,子shell程序不是子进程,而是同一进程中的另一个shell程序环境。

我使用一组基本的内置函数:

(echo "Hello";read)

在另一个终端上:

ps -t pts/0

PID TTY TIME CMD

20104 pts/0 00:00:00 ksh

因此,kornShell(ksh)中没有子进程。

输入bash,使用相同的命令,其行为似乎有所不同:

  PID TTY          TIME CMD

3458 pts/0 00:00:00 bash

20067 pts/0 00:00:00 bash

因此,bash中的一个子进程。 通过阅读bash手册页,很明显,为子shell创建了另一个进程,但是它伪造了$$,这很时髦。

是bash和ksh之间的区别是预期的,还是我错误地理解了症状?

编辑:附加信息:strace -f在Linux上的bash和ksh上运行显示,bashclone为示例命令调用了两次(它不调用fork)。所以bash可能正在使用线程(我试过了,ltrace但它的核心转储了!)。KornShell既不叫forkvfork也不clone

回答:

ksh93很难避免子外壳。部分原因是避免了stdio以及sfio的广泛使用,它允许内置程序直接进行通信。另一个原因是,ksh理论上可以具有这么多的内置函数。如果使用构建SHOPT_CMDLIB_DIR,则默认情况下将包括并启用所有cmdlib内置插件。我无法提供避免使用子shell的完整列表,但通常仅在使用内置插件且没有重定向的情况下使用。

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

出:

Version AJM 93v- 2013-02-22

31732 31732

31735 31732

31736 31732

31732 31732

31732 31732

31732 31732

31732 31732

31738 31732

所有这些内部I /

O处理的另一个很好的结果就是一些缓冲问题就消失了。这是读取带有teehead内置的代码行的有趣示例(请勿在任何其他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

以上是 bash和ksh之间的子shell差异 的全部内容, 来源链接: utcz.com/qa/402060.html

回到顶部