When I try this on my linux box, I get inconsistent results. Sometimes 123, sometimes (most of the times) 23, sometimes 12. This is a subtle buffering race condition between the rightmost xargs
and any of the sed
it spawns.
Dissecting the command line:
ls y*
will output 4 lines, y, y1, y2 and y3; buffering not relevant
xargs -i basename {}
will read them and launch, in a sequence, basename y
, basename y1
, basename y2
, basename y3
; output, same as input in our case, is line-buffered as each line comes from a different process.
xargs -i sed "s/{}//g"
, for each line X it reads (more on that later), launches sed "s/X//g"
- each
sed "s/X//g"
filters out each X it sees in the lines it reads
Where it gets tricky: the last two commands read input from the same stream. That stream is produced by multiple different processes in a sequence. Depending on a multitude of factors (system load, scheduling), the output could come out in very different timing patterns.
Let's suppose they're all very fast. Then all four lines might be available for the right xargs
to read in a single block. In that case, there would no input left for any of the sed
s to read, hence no output at all.
On the other hand, if they were very slow, there might be only one line available for the right xargs
on its first read attempt. That line would be "y". xargs
would spawn the first sed
as sed "s/y//g"
, which would consume all remaining input (y1, y2, y3), strip y
's, and output 1, 2, 3. Here's the same explanation again, with more explicit sequencing.
- first
basename
writes "y".
- right
xargs
reads "y", spawns sed s/y//g
. xargs
now waits for sed
to complete.
- second
basename
writes "y1"; sed
reads "y1", writes "1"
- third
basename
writes "y2"; sed
reads "y2", writes "2"
- fourth
basename
writes "y3"; sed
reads "y3", writes "3"
- left
xargs
is done; sed
reads EOF and stops
- right
xargs
tries to continue, reads EOF and stops
Not sure about my 12 case. Possibly GNU xargs
doesn't wait for its children to complete before it reads subsequent available input, and snatched the "y3" line from the first sed
.
In any case, you just set up a pipeline with multiple concurrent readers on the same writer, which yields mostly undeterministic results. To be avoided.
If you wanted operation on each of the files, it would be avoided by specifying a filename to use by sed
(note the final {}):
ls y* | xargs -i basename {} | xargs -i sed "s/{}//g" {}
If what you wanted was a cross-product-type result (strip each file name from each file), you'd need to arrange to have the file list produced as many times as there are files. Plus one for xargs
, if you still used that.
Hope this helps.