Is `xargs -t` the output of stderr or stdout, and can you control it?

Let's say I have a directory with hi.txt and blah.txt and I execute the following command on the linux-ish command line

ls *.* | xargs -t -i{} echo {}

the output you see is

echo blah.txt
blah.txt
echo hi.txt
hi.txt

      

I would like to redirect the output of stderr (say "echo blah.txt" doesn't work ...) leaving only the output of the xargs -t command written to std out, but that looks the same as stderr.

ls *.* | xargs -t -i{} echo {} 2> /dev/null

Is there a way to control it so that it is output to standard output?

+1


a source to share


5 answers


So, I believe you want to have as stdout

  • stdout from a utility executed by xargs
  • list of commands generated by xargs -t

You want to ignore the stderr stream generated by the executed utility.

Please correct me if I am wrong.

First, create a better testing utility:

% cat myecho
#!/bin/sh
echo STDOUT $@
echo STDERR $@ 1>&2
% chmod +x myecho
% ./myecho hello world
STDOUT hello world
STDERR hello world
% ./myecho hello world >/dev/null
STDERR hello world
% ./myecho hello world 2>/dev/null
STDOUT hello world
%

      

So now we have something that actually outputs for both stdout and stderr, so we can be sure that we only get what we want.



The tangential way to do this is not to use xargs, but rather make. Repeat the command and then do it however. This is his bag.

% cat Makefile
all: $(shell ls *.*)

$(shell ls): .FORCE
  ./myecho $@ 2>/dev/null

.FORCE:
% make
./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./myecho hi.txt 2>/dev/null
STDOUT hi.txt
% make >/dev/null
% 

      

If you are tied to using xargs, you need to change your utility that xargs uses to suppress stderr. Then you can use the trick 2>&1

others mentioned to move the command list generated by xargs -t from stderr to standard output.

% cat myecho2
#!/bin/sh
./myecho $@ 2>/dev/null
% chmod +x myecho2
% ./myecho2 hello world
STDOUT hello world
% ls *.* | xargs -t -i{} ./myecho2 {} 2>&1
./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./myecho hi.txt 2>/dev/null
STDOUT hi.txt
% ls *.* | xargs -t -i{} ./myecho2 {} 2>&1 | tee >/dev/null
%

      

So this approach works and folds whatever you want to output to stdout (apart from what you don't want).

If you do that much, you can write a generic utility to stop stderr:

% cat surpress_stderr
#!/bin/sh
$@ 2>/dev/null
% ./surpress_stderr ./myecho hello world
STDOUT hello world
% ls *.* | xargs -t -i{} ./surpress_stderr ./myecho {} 2>&1
./surpress_stderr ./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./surpress_stderr ./myecho hi.txt 2>/dev/null
STDOUT hi.txt
%

      

+2


a source


Using:

ls | xargs -t -i{} echo {} 2>&1 >/dev/null

      

2>&1

sends standard error to xargs

wherever standard output is currently going; >/dev/null

sends the original standard output to /dev/null

. Thus, the net result is that standard output contains echo commands, but /dev/null

file names. We can discuss spaces in filenames and whether it would be easier to use a sed

script to place an "echo" at the front of each line (no option -t

) or you can use:

ls | xargs -i{} echo echo {}

      

(Tested: Solaris 10, Korn Shell, should work on other shells and Unix platforms.)


If you don't mind seeing the inner workings of commands, I've managed to separate the error output from xargs

and the error output of the command being executed.

al * zzz | xargs -t 2>/tmp/xargs.stderr -i{} ksh -c "ls -dl {} 2>&1"

      

Command (non-standard) al

lists its arguments, one per line:

for arg in "$@"; do echo "$arg"; done

      

The first redirect ( 2>/tmp/xargs.stderr

) sends the error output from xargs

to a file /tmp/xargs.stderr

. The command executed is: " ksh -c "ls -dl {} 2>&1"

", which uses the Korn shell to run ls -ld

on a filename, with any error output being printed to standard output.

The output in /tmp/xargs.stderr

looks like this:

ksh -c ls -dl x1 2>&1
ksh -c ls -dl x2 2>&1
ksh -c ls -dl xxx 2>&1
ksh -c ls -dl zzz 2>&1

      



I used ' ls -ld

' instead echo

, to make sure I tested errors - files x1

, x2

and xxx

there, but zzz

does not work.

The standard output looked like this:

-rw-r--r--   1 jleffler rd          1020 May  9 13:05 x1
-rw-r--r--   1 jleffler rd          1069 May  9 13:07 x2
-rw-r--r--   1 jleffler rd            87 May  9 20:42 xxx
zzz: No such file or directory

      

When run without a command wrapped in ' ksh -c "..."

', the I / O redirection was passed as a command argument (' ls -ld

'), and so it reported that it could not find the file ' 2>&1

". That is, xargs

it did not use the shell to redirect the I / O itself.

Various other redirects could be arranged, but the main problem is that it xargs

does not provide for separating its own error output from the command it is executing, so this is difficult to do.

Another fairly obvious option is to use a xargs

script to write a shell and then execute the shell. This is the variation I showed earlier:

ls | xargs -i{} echo echo {} >/tmp/new.script

      

Then you can see the commands with:

cat /tmp/new.script

      

You can run commands to throw errors:

sh /tmp/new.script 2>/dev/null

      

And, if you don't want to see standard output from commands, add 1>&2

to the end of the command.

+3


a source


It looks like xargs -t goes to stderr and you can't help it.

You can do:

ls | xargs -t -i{} echo "Foo: {}" >stderr.txt | tee stderr.txt

to only display stderr data in your terminal as you run your command, then grep through stderr.txt after seeing if anything unexpected happened, on the lines grep -v Foo: stderr.txt

Also note that on Unix, ls *.*

this is not how you display everything. If you want to view all files, just run it ls

yourself.

+1


a source


xargs -t

echos commands to be executed in stderr before being executed. If you want them instead of echoing to stderr, you can pipe stderr to stdout with the construct 2>&1

:

ls *.* | xargs -t -i{} echo {} 2>&1

      

+1


a source


As I understand, your problem using GNU Parallel http://www.gnu.org/software/parallel/ will do the right thing:

ls *.* | parallel -v echo {} 2> /dev/null

      

0


a source







All Articles