bash和KSH之间的子壳差异(Sub-shell differences between bash

2019-07-19 05:15发布

我始终认为,一个子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创建,但其假货$$,这是sneeky。

这是Bash和ksh之间的差异预期,还是我读的症状是否有误?

编辑:附加信息:运行strace -f在bash和ksh的Linux上显示的bash调用clone两次样本命令(它不叫fork )。 所以庆典可能会使用线程(我试过ltrace但内核倾倒!)。 KornShell要求既不forkvfork ,也不是clone

Answer 1:

ksh93的工作异常辛苦,避免子shell。 的部分原因是标准输入输出和广泛使用的避免了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


Answer 2:

KSH中,一个子shell可能会或可能不会产生一个新的进程。 我不知道是什么条件,但外壳是对系统性能进行了优化,其中fork()是比它通常是在Linux上比较昂贵,所以它避免了创建新的进程时,它可以。 该规范说“新环境”,但环境分离可能在过程来完成。

另一种隐约相关的区别是使用的管道新工艺。 在KSH和zsh的,如果在管道中的最后一个命令是一个内置的,它运行在当前的shell进程,所以此工程:

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

在bash中,第一个是在子shell运行,所以上面毕竟管道命令不工作:

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

$

作为@大卫-汤普森,085分满分,你可以,如果你关闭作业控制(获得在bash版本4.2和较新的KSH / zsh的行为set +o monitor ),并打开lastpipe选项( shopt -s lastpipe )。 但我平时解决方案是使用进程替换,而不是:

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


Answer 3:

bash的手册页上写着:

管道中的每个命令被作为一个单独的处理中执行(即,在一个子shell)。

虽然这句话是对的管道,它强烈地暗示一个子shell是独立的过程。

维基百科的消歧义页还介绍了儿童的过程项的子shell。 子进程肯定本身是一个过程。

该手册页KSH(一览)不是直接对自己的子shell的定义,所以这并不意味着一个或其他方式,一个子shell是一个不同的过程。

学习Korn Shell配合说,他们是不同的程序。

我说你失去了一些东西(或这本书是错误或过时的)。



文章来源: Sub-shell differences between bash and ksh