Skip to content

Instantly share code, notes, and snippets.

@cellularmitosis
Last active May 8, 2025 15:56
Show Gist options
  • Save cellularmitosis/bf8b8c69a3ec015f12921b92025700a8 to your computer and use it in GitHub Desktop.
Save cellularmitosis/bf8b8c69a3ec015f12921b92025700a8 to your computer and use it in GitHub Desktop.
A stdin-stdout passthrough which ignores SIGPIPE

Blog 2025/5/8

<- previous | index

A stdin-stdout passthrough which ignores SIGPIPE

One of the problems with using bash's set -o pipefail is that a command may fail with SIGPIPE if the next command in the pipepine exits before receiving all of its input.

For example:

command_which_creates_lots_of_output | grep -q foo

Here, grep will exit as soon as it encounters the first instance of "foo" on stdin.

If command_which_creates_lots_of_output still has more output to write, it will receive SIGPIPE, causing the pipeline to fail.

We can work around this by writing a C program which copies stdin to stdout but ignores SIGPIPE.

Compiling:

gcc -std=c99 -Wall -Werror -o nosigpipe nosigpipe.c

Revisiting our above example:

command_which_creates_lots_of_output | ./nosigpipe | grep -q foo
/* a stdin-stdout passthrough which ignores SIGPIPE. */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
bool did_epipe = false;
#define BUFF_SIZE 1024
char* buff[BUFF_SIZE];
void sigpipe_handler(int signum) {
; /* do nothing. */
}
int main(void) {
/* register to receive SIGPIPE. */
int ret = 0;
struct sigaction sa;
ret = sigemptyset(&sa.sa_mask);
if (ret == -1) {
perror("sigemptyset");
exit(1);
}
sa.sa_handler = sigpipe_handler;
ret = sigaction(SIGPIPE, &sa, NULL);
if (ret == -1) {
perror("sigaction");
exit(1);
}
/* copy stdin to stdout. */
ssize_t nr;
ssize_t nw;
while (true) {
nr = read(STDIN_FILENO, buff, BUFF_SIZE);
if (nr == -1) {
perror("read");
exit(1);
}
if (nr == 0) {
exit(0);
}
if (did_epipe) {
/* if we've hit SIGPIPE, read and discard any remaining input. */
continue;
}
nw = 0;
while (nr > 0) {
nw = write(STDOUT_FILENO, buff + nw, nr);
if (nw == -1) {
if (errno == EINTR) {
errno = 0;
continue;
}
if (errno == EPIPE) {
did_epipe = true;
errno = 0;
break;
}
perror("write");
exit(1);
}
nr -= nw;
nw = 0;
continue;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment