简体   繁体   English

Bash:使用bash脚本的头部和尾部行为

[英]Bash: Head & Tail behavior with bash script

Suppose I have following script:- 假设我有以下脚本: -

test.sh test.sh

#!/bin/bash
command1  #prints 5 lines
command2  #prints 3 lines

I run the script with test.sh|head -n5 我用test.sh|head -n5运行脚本

What will happen in this case? 在这种情况下会发生什么? Will it run both the commands? 它会运行这两个命令吗? or will it stop after command1? 还是会在command1之后停止? What if I call it with -n1 ? 如果我用-n1调用它怎么办?

Background: I might be asking a very basic question, but I actually noticed something interesting. 背景:我可能会问一个非常基本的问题,但实际上我注意到了一些有趣的东西。 My script(different one) was processing 7,000 files and each file produces 1 line of output. 我的脚本(不同的)处理了7,000个文件,每个文件产生1行输出。 It takes 7 minutes to run the script completely but doing head -n1 gave me prompt immediately like the script has terminated after processing first file only 完全运行脚本需要7分钟,但是在处理完第一个文件后,执行head -n1会立即提示我脚本已经终止

Edit: Following is my script 编辑:以下是我的脚本

for i in $(ls filepath);do
     echo "$i" # issue here
    python mySript "$i" > "/home/user/output/""$i"".out"
  fi
done

Removing echo above enables the script to run full 7 minute with head -n1 , but with echo it just prints first line then exit. 删除上面的回声使脚本能够使用head -n1运行整整7分钟,但是使用echo它只打印第一行然后退出。

This is a fairly interesting issue! 这是一个相当有趣的问题! Thanks for posting it! 谢谢发帖!

I assumed that this happens as head exits after processing the first few lines, so SIGPIPE signal is sent to the running the script when it tries to echo $x next time. 我假设这是在处理前几行之后出现head退出,因此SIGPIPE信号在下次尝试echo $x时发送到运行脚本的 I used RedX's script to prove this theory: 我使用RedX的脚本来证明这个理论:

#!/usr/bin/bash
rm x.log
for((x=0;x<5;++x)); do
    echo $x
    echo $x>>x.log
done

This works, as You described! 这就像你描述的那样有用! Using t.sh|head -n 2 it writes only 2 lines to the screen and to x.log. 使用t.sh|head -n 2 ,它只向屏幕和x.log写入2行。 But trapping SIGPIPE this behavior changes... 但陷阱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

Output: 输出:

$ ./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

The write error occurs as stdout is already closed as the other end of the pipe is closed. 当管道的另一端关闭时, stdout已经关闭,因此发生写入错误。 And any attempt to write to the closed pipe causes a SIGPIPE signal, which terminates the program by default (see man 7 signal ). 任何写入闭合管道的尝试都会产生一个SIGPIPE信号,默认情况下会终止该程序(参见man 7 signal )。 The x.log now contains 5 lines. x.log现在包含5行。

This also explains why /bin/echo solved the problem. 这也解释了为什么/bin/echo解决了这个问题。 See the following script: 请参阅以下脚本:

rm x.log
for((x=0;x<5;++x)); do
    /bin/echo $x
    echo "Ret: $?">&2
    echo $x>>x.log
done

Output: 输出:

$ ./t.sh |head -n 2
0
Ret: 0
1
Ret: 0
Ret: 141
Ret: 141
Ret: 141

Decimal 141 = hex 8D. 十进制141 =十六进制8D。 Hex 80 means a signal was received, hex 0D is for SIGPIPE. 十六进制80表示接收到信号,十六进制0D表示SIGPIPE。 So when /bin/echo tried to write to stdout it got a SIGPIPE and it was terminated (as default behavior) instead of the running the script. 因此,当/bin/echo尝试写入stdout时,它获得了一个SIGPIPE并且它被终止(作为默认行为)而不是运行脚本的

Nice finding. 很好的发现。 According to my tests it's exactly like you said. 根据我的测试,它就像你说的那样。 For example I have this script that just eats cpu, to let us spot it in top : 例如,我有这个只吃epu的脚本,让我们在top发现它:

for i in `seq 10`
  do echo $i
  x=`seq 10000000`
done

Piping the script with head -n1 we see the command returning after the first line. 使用head -n1管道脚本,我们看到在第一行之后返回的命令。 This is the head behavior: it completed its work, so it can stop and return the control to you. 这是head行为:它完成了它的工作,所以它可以停止并将控制返回给你。

The input script should continue running but look what happens: when the head returns, its pid doesn't exist anymore. 输入脚本应该继续运行,但看看会发生什么: head返回时,它的pid不再存在。 So when linux tries to send the output of the script to the head process, it does not find the process, so the script crashes and stops. 因此,当linux尝试将脚本的输出发送到head进程时,它找不到进程,因此脚本崩溃并停止。

Let's try it with a python script: 让我们用python脚本尝试一下:

for i in xrange(10):
    print i
    range(10000000)

When running it and piping to head you have this: 当它运行并且管道到头时你有这个:

$ 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

The -u option tells python to automatically flush the stdin and stdout, as bash would do. -u选项告诉python自动刷新stdin和stdout,就像bash那样。 So you see that the program actually stops with an error. 所以你看到程序实际上因错误而停止。

This is more of a comment then an answer but it is too big for a comment. 这更像是一个评论,然后是答案,但它对于评论来说太大了。

I tried following script: 我试过以下脚本:

#!/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"

Then i ran the script with: 然后我运行脚本:

./test_head.sh | ./test_head.sh | head -n1 头-n1

The cat output is (to my surprise): 猫的输出是(令我惊讶的):

1 line 1行

I have no idea what is going on. 我不知道发生了什么事。

After reading @ymonad comment i tried it out and replace echo with /bin/echo and that solved the problem. 阅读@ymonad评论后,我试了一下,用/bin/echo替换了echo ,解决了这个问题。 I hope he can explain more about this behaviour. 我希望他能解释一下这种行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM