为什么我的bash脚本会阻塞?

时间:2015-10-02 04:37:40

标签: bash

我有一个bash脚本:my.sh

#!/bin/bash

do_sth()
{
    sleep 5
}

main()
{
    do_sth >/dev/null &
    echo do sth in background ...
}

if [ "$1" = "1st_way" ]; then
    main
elif [ "$1" = "2nd_way" ]; then
    main >/dev/null
fi

以下命令立即返回

./my.sh 1st_way | cat

但是,以下命令会阻塞5秒

./my.sh 2nd_way | cat

我想知道为什么它会以第二种方式阻挡5秒钟。

1 个答案:

答案 0 :(得分:5)

考虑这个脚本:

#!/bin/bash

main() {
    cd /tmp
}

main > /dev/null
echo goodbye

在bash运行main函数之前,它需要将自己的标准输出重定向到/dev/null。然后它需要运行main函数。然后,在执行echo命令之前,需要将其标准输出恢复为重定向之前的任何值。

这是如何做到的。要进行重定向,它会打开/dev/null,获取(让他们说)文件描述符3.然后它将其文件描述符1(标准输出)复制到第一个可用的fd≥10,得到(让&#39) ; s说)10。然后它复制fd 3(在/dev/null上打开)到fd 1,并关闭fd 3。

稍后,要撤消重定向,它会将fd 10(在bash的原始标准输出上打开)复制到fd 1,然后关闭fd 10。

好的,现在回到你的脚本。当你的脚本显示main >/dev/null时,所有相同的事情都会发生,所以当main执行时,fd 10在shell的原始标准输出上打开,这是管道的可写端到{ {1}}。

当您的catmain时,shell会分叉。子shell继承fd 10,因此父shell和子shell都在管道上打开了fd 10。 (注意shell知道它不需要在这里保存它的标准输出,因为do_sth >/dev/null &使它成为分叉。)

&中的子shell运行do_sth时,它会运行sleep并等待sleep退出。因此,子shell会挂起五秒钟,在此期间它会在管道的可写端打开fd 10。

sleep进程在管道的可读端读取EOF,直到可写端的所有文件描述符都关闭为止,直到该子shell退出时才会发生这种情况。直到cat退出才会发生。