Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

In this example:

$ for i in {1..3}; do sleep 1; echo $i;  done | head -n 2

why is the first command (for loop) killed only just before the 3 is displayed ? I expected it to be killed right after the 2 is displayed.

The problem I was trying to solve initially was this one:

$ for i in *(/); do c=`find "$i" -iname "*.ext" | wc -l`; echo "$c  $i"; done | head -n 3

I expected it to end as soon as three lines are on the screen, but it's running the loop four times, discarding the fourth line.

What I thought would happen:

sleep 1
echo 1
sleep 1
echo 2 
# 2 is printed and the for loop is killed

What actually happens:

sleep 1
echo 1
sleep 1
echo 2 
sleep 1
echo 3
# 3 is not printed and the for loop is killed

Is there a way to abort the loop immediately after a given number of iterations without adding some logic inside the loop itself ?

share|improve this question

2 Answers 2

up vote 1 down vote accepted

A SIGPIPE is only sent when a program tries to write to a closed pipe. It would be pretty bad to send SIGPIPE otherwise: programs that happen to have a pipe open may not even be aware of it, and should not be killed if they never interact with the pipe.

In for i in 1 2 3; do sleep 1; echo $i; done, the sleep command doesn't write anything, so it won't receive a signal. It's only when echo runs that a SIGPIPE is sent.

In general, SIGPIPE isn't designed for precision work. It's only meant to ensure that programs whose job is to produce some output don't keep running forever when the consumer of that output goes away. In most practical scenarios, the program on the left-hand side of the pipe buffers writes, and it will only receive a SIGPIPE the next time it flushes its buffer.

If you want to stop a loop after a number of iterations, build that logic into the loop.

share|improve this answer

On my different shells (ksh, zsh, bash), I have the expected bahaviour : i only have 1, 2, not 3. The issue here may be related on how the SIGPIPE is handled on your ZSH version.

Can you try running these commands withing an "env-free" instance ? eg, without alias, configuration, etc...

As put in the comments, the shell receive an error when he wants to echo something (with the write() function). As long as the whole pipe chain is running (the head process is still running and have his STDIN open), everything goes fine. But when the head terminates, the output of your shell will go nowhere (broken pipe).

But until the shell tries to write something in his stdout, he's not aware the pipe is broken and continues his processing.

Thus, you'll see the "echo 3" in debugging, which will fail and terminates the shell.

share|improve this answer
    
The question is: why doesn't it stop right after 2 is displayed. I want the loop to run two times, not three. –  Antoine Lecaille Nov 12 '13 at 8:51
    
Look on the other way: the first process (your shell) should stops because the piped process (head) terminates and closes the STDIN. Then, when your shell wants to echo in STDOUT (which was piped to the terminated "head") the write() function of the shell will return -1 with error 'EPIPE'. This error in turn terminates the shell. If you have an error management system in zsh, or any configuration or trap, the output may still be echoed in your shell. –  Adrien M. Nov 12 '13 at 8:59
    
OK. So tail closes the pipe on time, but head doesn't know it yet.. –  Antoine Lecaille Nov 12 '13 at 9:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.