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.

I am using a ffi for nodejs which for the most part has nothing to do with this question which is really about understanding pipes better, but does offer some context

function exec(cmd) {
  var buffer = new Buffer(32);
  var result = '';
  var fp = libc.popen('( ' + cmd + ') 2>&1', 'r');
  var code;

  if (!fp) throw new Error('execSync error: '+cmd);

  while( !libc.feof(fp) ){
    libc.fgets(buffer, 32, fp)
    result += buffer.readCString();
  }
  code = libc.pclose(fp) >> 8;

  return {
    stdout: result,
    code: code
  };
}

which brings me to this bit of code that when i run using this exec function

 tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}

I get the error

write error: Broken pipe
tr: write error

but I do get the output I expect 8 random numbers, this confused the hell out of me but then in some wild googling I found this stack answer which perfectly fit my situation.

I am left with these questions though...

Why does

tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}

throw a broken pipe error when called with my exec command but not when called from the shell. I don`t understand why when I call

tr -dc "[:alpha:]" < /dev/urandom

it reads endlessly but when I pipe it to

head -c ${1-8}

It works with out throwing a broken pipe error. It seems that head would take what it needs and tr would just read for ever. At least it should throw broken pipe, head would consume the first 8 bytes and then tr would still be putting out output and broken pipe would be thrown by tr because head has stopped running.

Both situations make sense to me but it seems that they are some what exclusive to each other. I don`t understand what is different between calling

exec(tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8})

and

tr -dc "[:alpha:]" < /dev/urandom | head -c ${1-8}

directly from the command line

and specifically why < an endless file into something and then | it to something makes it not run endlessly, ive been doing this for years and never questioned why it works this way.

Lastly is it ok to ignore this broken pipe err? Is there a way to fix it am I doing something wrong in my c++ ish javascript code. Am I missing some kind of popen basics?

------ EDIT

messing around some more the code

exec('head -10 /dev/urandom | tr -dc "[:alpha:]" | head -c 8')

throws no pipe error!

share|improve this question

1 Answer 1

up vote 5 down vote accepted

Normally, tr shouldn't be able to write that error message because it should have been killed by a SIGPIPE signal when trying to write something after the other end of the pipe has been closed upon termination of head.

You get that error message because somehow, the process running tr has been configured to ignore SIGPIPEs. I suspect that might be done by the popen() implementation in your language there.

You can reproduce it by doing:

sh -c 'trap "" PIPE; tr -dc "[:alpha:]" < /dev/urandom | head -c 8'

You can confirm that's what is happening by doing:

strace -fe signal sh your-program

(or the equivalent on your system if not using Linux). You'll then see something like:

rt_sigaction(SIGPIPE, {SIG_IGN, ~[RTMIN RT_1], SA_RESTORER, 0x37cfc324f0}, NULL, 8) = 0

or

signal(SIGPIPE, SIG_IGN)

done in one process before that same process or one of its descendants executes the /bin/sh that interprets that command line and starts tr and head.

If you do a strace -fe write, you'll see something like:

write(1, "AJiYTlFFjjVIzkhCAhccuZddwcydwIIw"..., 4096) = -1 EPIPE (Broken pipe)

The write system call fails with an EPIPE error instead of triggering a SIGPIPE.

In any case tr will exit. When ignoring SIGPIPE, because of that error (but that also triggers an error message). When not, it exits upon receiving the SIGPIPE. You do want it to exit, since you don't want it carrying on reading /dev/urandom after those 8 bytes have been read by head.

To avoid that error message, you can restore the default handler for SIGPIPE with:

trap - PIPE

Prior to calling tr:

popen("trap - PIPE; { tr ... | head -c 8; } 2>&1", ...)
share|improve this answer

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.