3

If I simply run read it reads one line and immediately exits when the Enter is pressed.

$ read
typing something here
$

However, when I pass the input through a pipe, e.g. from cat, read behaves differently and keeps running until it encounters the second newline:

$ cat | read
typing first line
typing second line
$

Can anyone explain why it is this way?

PS: This question was inspired by How to feed standard input and dump into file at the same time?

1 Answer 1

2

It doesn't have anything to do with the newline.

If you run your command using strace, you will find out that the cat will receives a SIGPIPE at the end, before being closed:

$ strace cat | read

...
someOutput
...
+++ killed by SIGPIPE +++
  1. First cat command gets to run .
  2. Then you type something in for the first time and hit the Enter.
  3. What you have typed will be piped to the read.
  4. cat is still running and waiting for a EOF.
  5. You type something else and then hit the Enter agian.
  6. This time, it can't be piped to read, because there is no read anymore waiting for input (it has been closed after first pipe) unless you run it like this:

    cat | while read line; do echo $line; done;
    
  7. cat will receives a SIGPIPE and gets closed.

A process receives a SIGPIPE when it attempts to write to a pipe (named or not) or socket of type SOCK_STREAM that has no reader left. [1]

Receiving SIGPIPE happens after when second pipe happens.

For example consider yes command, because a command like yes pipes something fast and repeatedly:

yes | read

it gets closed immediately after the second pipe, pay attention to the two write() calls:

close(3)                                = 0                    
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 8192) = 8192          
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 8192) = -1 EPIPE (Broken pipe) 
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=3542, si_uid=1000} ---
+++ killed by SIGPIPE +++

Although, because yes command is too fast you might see more than of two write() calls, however if you run it several times you will see at least two calls and never one.

6
  • So if I understand you correctly, this has something to do with line buffering, i.e. cat only tries to send whole lines through the pipe as soon as the ENTER key gets pressed. read exits after the first line already, but cat only notices that when it tries to use the pipe again, not earlier?
    – Byte Commander
    Sep 2, 2017 at 9:33
  • Yes, the second time when it tries to pipe the second line it receives a SIGPIPE and get closed.
    – Ravexina
    Sep 2, 2017 at 9:36
  • Uhm... but I just found out that cat actually isn't line buffered at all: superuser.com/a/896684/418736 - now I'm confused.
    – Byte Commander
    Sep 2, 2017 at 9:45
  • Have you tried running strace cat | read? Or running strace cat and sending a SIGPIPE (kill -13 ) to its PID? I'm not sure about the buffering, but from inspecting the strace outputs, that is how it's working.
    – Ravexina
    Sep 2, 2017 at 9:59
  • 1
    Try this: cat <whatever> | grep ^ --line-buffered and use it in place of cat.
    – user595510
    Sep 2, 2017 at 10:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .