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", ...)
I have a solution that uses lsof
. It is not installed on BSD by default so if anyone want to use it on BSD, it is required to install it.
Make a shell script:
#!/bin/sh
lsof -p $1 | grep cwd | awk '{print $9}'
Copy it to a directory in your path. It prints the working directory of PID given in the first argument, I.E.
$ script 1987
/home/enedil
Best Answer
The point that you may be missing – that many people have trouble with, especially if they have experience with other operating systems before they come to *nix – is that, in many other OSs, wildcards on the command line are normally passed to the command to process as it sees fit. For example, in Windows Command Prompt,
Whereas, in *nix, in order to simplify the job of the programmer(s) of the individual commands (e.g.,
mv
), wildcards (when not quoted or escaped) are handled by the shell, in a manner that’s independent of the interface and functionality of the command that the wildcards are arguments to. Most programs that take filenames as command-line arguments expect those files to exist (yes,mkdir
andtouch
are exceptions to that rule, as aremkfifo
,mknod
, and, to a partial extent,cp
,ln
,mv
, andrename
; and there are probably others), so it doesn’t really make sense for the shell to expand wildcards to names for files that don’t exist. And for the shell (and, by that, I mean every shell – Bourne, bash, csh, fish, ksh, zsh, etc…) to handle the exceptions differently would probably be too much of a hassle.That said, there are a couple of ways to get a result like what you want. If you know what the wildcard is going to expand to, and it’s not long, you can generate it with brace expansion:
A more general solution:
Gilles helped me find another way to do it in bash.
Obviously
benbradley
is just an identifier here; you can use any name (e.g., any single letter). It took me a couple of tries to get this right, so let me break it down:identifier=value
creates a (scalar) variable namedidentifier
(if it doesn’t already exist) and assigns the valuevalue
to it. For example,G=Man
. You can reference a scalar variable with$identifier
; for example,$G
, or, more safely, as${identifier}
. For example,$Gage
and$Gilla
may be undefined, but${G}age
isManage
and${G}illa
isManilla
.identifier=(value1 value2 … )
creates an array variable namedidentifier
(if it doesn’t already exist) and assigns the listed values to it. For example,
or$name
will reference the first element (and so is fairly useless). You can reference an arbitrary element as${name[subscript]}
; for example,${spectrum[0]}
isred
and${spectrum[4]}
isblue
. And you can use${name[@]}
and${name[*]}
to reference the entire array.So, here it is in pieces:
${parameter/pattern/string}
expands${parameter}
and replaces the longest match ofpattern
withstring
.pattern
begins with#
, it must match at the beginning of the expanded value ofparameter
. Ifpattern
begins with%
, it must match at the end of the expanded value ofparameter
. In other words,
acts like(yes, that seems a little backwards). So a
pattern
that is just a%
is like a regular expression that is just a$
– it matches the end of the input string (search space).string
of/test
. So this replaces the end of the parameter with/test
(i.e., it appends/test
to the parameter).${parameter/pattern/string}
that’s non-intuitive is that it always ends with a}
. It need not, and cannot, end with a third/
. Therefore, a/
instring
cannot be interpreted as a delimiter, and therefore we can have astring
of/test
without needing to escape the/
.parameter
is an array variable subscripted with@
or*
, the substitution operation is applied to each member of the array in turn.${name[@]}
(rather than${name[*]}
) and put the results in double quotes, you preserve the integrity of the elements of the array (i.e., you preserve spaces and other special characters in them) without combining the separate elements into one long word.