0

If the yes command echos "y" continuously until killed, then it will never get done right? If it will never get done then how does it pipe it's output to the next command??

yaquawa
  • 111
  • 1
    You're probably assuming that the commands in a pipeline are executed sequentially. That's not the case. They're run in parallel, at the same time. Just like my dog is not waiting me to do die before eating the food I had put for him in the bowl, the pipe consumer is not waiting for the pipe producer to die before consuming a line the producer has put into the pipe. –  Mar 30 '20 at 02:00
  • @muru find something better. –  Mar 30 '20 at 02:03

1 Answers1

3

The yes program will write to the pipe, concurrently with the reader. If the pipe is empty, the reader will block in the kernel in a call to read() waiting for more input. If the pipe is full, the write will block in the kernel in a call to write() waiting for the reader to free up some space in the pipe.

The kernel delivers a SIGPIPE signal to a process if it ties to write to a pipe that has no reader. When the reading process closes the read-end of the pipe (either explicilty or as a result of its termination), the next time the writing process tries to write to the pipe, it will receive the SIGPIPE signal.

To illustrate this point, consider this simplified version of the yes program that prints a continuous stream of y. This program differs from yes in that it generates a message when it receives the SIGPIPE signal:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

static void handler(int sig) {
    #define msg "received SIGPIPE signal, terminating\n"
    write(2, msg, sizeof msg);
    exit(1);
    #undef msg
}

int main(void) {
    // Instruct the kernel to call the handler() function when
    // this process receives the SIGPIPE signal.  The default behavior
    // when this signal is received is for the process to terminate.
    signal(SIGPIPE, handler);

    for (;;) {
        if (write(1, "y\n", 2) < 0) {
            fprintf(stderr, "%s\n", strerror(errno));
            return 2;
        }
    }
    return 0;
}

I can compile and run the program and see that it behaves like yes:

$ gcc ex.c -o myes

$ ./myes
y
y
y
...

If I pipe the output to another process, when that process terminates (and the read-end of the pipe is closed), the myes program receives the SIGPIPE signal (as indicated by the associated message).

$ ./myes | head -n5
y
y
y
y
y
received SIGPIPE signal, terminating
Andy Dalton
  • 13,993
  • Thanks for the example code! Does it mean the yes command generates tons of of "y" while the next command is executing but not generating on demand?(even if the next command only required one ‘y’ the yes command could output tons of unneeded “y” ??) – yaquawa Mar 31 '20 at 03:14
  • 1
    That's correct. The pipe is a bounded memory buffer within the kernel. The writer (here the yes command) writes to that buffer. The reader reads from the buffer. They run concurrently without any explicit knowledge of one another. If the reader wants/needs/uses only one y, yes will write as many as it can to the buffer until either (1) the buffer is full or (2) it receives the SIGPIPE signal on one of the write() operations after the reader closes the pipe. – Andy Dalton Mar 31 '20 at 13:22