我有一个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秒钟。
答案 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}}。
当您的cat
说main
时,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
退出才会发生。