假设我有以下脚本: -
test.sh
#!/bin/bash
command1 #prints 5 lines
command2 #prints 3 lines
我使用test.sh|head -n5
在这种情况下会发生什么?它会运行这两个命令吗?还是会在command1之后停止? 如果我用 -n1 调用它?
背景:我可能会问一个非常基本的问题,但实际上我注意到了一些有趣的事情。我的脚本(不同的)处理了7,000个文件,每个文件产生1行输出。完全运行脚本需要7分钟,但执行 head -n1 会立即提示我,因为脚本在处理完第一个文件后已经终止
修改 以下是我的剧本
for i in $(ls filepath);do
echo "$i" # issue here
python mySript "$i" > "/home/user/output/""$i"".out"
fi
done
删除上面的 echo 可让脚本在 head -n1 下运行整整7分钟,但使用echo它只会打印第一行然后退出。
答案 0 :(得分:17)
这是一个相当有趣的问题!谢谢发帖!
我认为在处理完前几行后,head
退出时会发生这种情况,因此SIGPIPE
信号在尝试echo $x
时会发送到运行脚本的bash下次。我使用RedX的脚本来证明这个理论:
#!/usr/bin/bash
rm x.log
for((x=0;x<5;++x)); do
echo $x
echo $x>>x.log
done
这就像你描述的那样有用!使用t.sh|head -n 2
,它只向屏幕和x.log写入2行。但陷阱SIGPIPE这种行为改变了......
#!/usr/bin/bash
trap "echo SIGPIPE>&2" PIPE
rm x.log
for((x=0;x<5;++x)); do
echo $x
echo $x>>x.log
done
输出:
$ ./t.sh |head -n 2
0
1
./t.sh: line 5: echo: write error: Broken pipe
SIGPIPE
./t.sh: line 5: echo: write error: Broken pipe
SIGPIPE
./t.sh: line 5: echo: write error: Broken pipe
SIGPIPE
由于管道的另一端已关闭,stdout
已关闭,因此发生写入错误。任何写入闭合管道的尝试都会产生SIGPIPE信号,默认情况下会终止该程序(参见man 7 signal
)。 x.log现在包含5行。
这也解释了为什么/bin/echo
解决了这个问题。请参阅以下脚本:
rm x.log
for((x=0;x<5;++x)); do
/bin/echo $x
echo "Ret: $?">&2
echo $x>>x.log
done
输出:
$ ./t.sh |head -n 2
0
Ret: 0
1
Ret: 0
Ret: 141
Ret: 141
Ret: 141
十进制141 =十六进制8D。十六进制80表示接收到信号,十六进制0D表示SIGPIPE。因此,当/bin/echo
尝试写入stdout时,它获得了一个SIGPIPE并且它被终止(作为默认行为)而不是运行该脚本的bash。
答案 1 :(得分:8)
很好的发现。根据我的测试,它就像你说的那样。例如,我有这个只吃epu的脚本,让我们在top
中找到它:
for i in `seq 10`
do echo $i
x=`seq 10000000`
done
使用head -n1
管道脚本,我们看到在第一行之后返回的命令。这是head
行为:它完成了它的工作,所以它可以停止并将控件返回给你。
输入脚本应继续运行但看看会发生什么:当head
返回时,其 pid 不再存在。因此,当linux尝试将脚本的输出发送到head进程时,它找不到进程,因此脚本崩溃并停止。
让我们用python脚本尝试:
for i in xrange(10):
print i
range(10000000)
当它运行并且管道到头时你有这个:
$ python -u test.py | head -n1
0
Traceback (most recent call last):
File "test.py", line 2, in <module>
print i
IOError: [Errno 32] Broken pipe
-u
选项告诉python自动刷新stdin和stdout,就像bash那样。所以你看到程序实际上因错误而停止。
答案 2 :(得分:1)
这更像是一个评论,然后是一个答案,但它对评论来说太大了。
我尝试了以下脚本:
#!/usr/bin/env bash
rm -f "test_head.log"
echo "1 line"
echo "1 line" >> "test_head.log"
echo "2 line"
echo "2 line" >> "test_head.log"
echo "3 line"
echo "3 line" >> "test_head.log"
echo "4 line"
echo "4 line" >> "test_head.log"
echo "5 line"
echo "5 line" >> "test_head.log"
echo "6 line"
echo "6 line" >> "test_head.log"
echo "7 line"
echo "7 line" >> "test_head.log"
echo "8 line"
echo "8 line" >> "test_head.log"
然后我用:
运行脚本./ test_head.sh |头-n1
猫的输出是(令我惊讶的):
1行
我不知道发生了什么。
阅读@ymonad评论后,我尝试了将echo
替换为/bin/echo
,这解决了问题。我希望他能解释一下这种行为。