Bash: Head & Tail behavior with bash script

后端 未结 3 1042
北荒
北荒 2020-12-29 04:49

Suppose I have following script:-

test.sh

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

I run th

3条回答
  •  伪装坚强ぢ
    2020-12-29 05:38

    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 bash running the script when it tries to echo $x next time. I used RedX's script to prove this theory:

    #!/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. But trapping SIGPIPE this behavior changes...

    #!/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. And any attempt to write to the closed pipe causes a SIGPIPE signal, which terminates the program by default (see man 7 signal). The x.log now contains 5 lines.

    This also explains why /bin/echo solved the problem. 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. Hex 80 means a signal was received, hex 0D is for SIGPIPE. So when /bin/echo tried to write to stdout it got a SIGPIPE and it was terminated (as default behavior) instead of the bash running the script.

提交回复
热议问题