Bash:Head&使用bash脚本进行尾部行为

时间:2014-10-20 08:06:22

标签: linux bash

假设我有以下脚本: -

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它只会打印第一行然后退出。

3 个答案:

答案 0 :(得分:17)

这是一个相当有趣的问题!谢谢发帖!

我认为在处理完前几行后,head退出时会发生这种情况,因此SIGPIPE信号在尝试echo $x时会发送到运行脚本的下次。我使用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并且它被终止(作为默认行为)而不是运行该脚本的

答案 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,这解决了问题。我希望他能解释一下这种行为。