Search code examples
command-linexargs

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


say i have a directory with hi.txt and blah.txt and i execute the following command on a linux-ish command line

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

the output you will see is

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

i'd like to redirect the stderr output (say 'echo blah.txt' fails...), leaving only the output from the xargs -t command written to std out, but it looks as if it's stderr as well.

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

Is there a way to control it, to make it output to stdout?


Solution

  • So I believe what you want is to have as stdout is

    • the stdout from the utility that xargs executes
    • the listing of commands generated by xargs -t

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

    Please correct me if I'm wrong.

    First, let's 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 to both stdout and stderr, so we can be sure we're only getting what we want.

    A tangential way to do this is not to use xargs, but rather, make. Echoing a command and then doing it is kind of what make does. That's its 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're tied to using xargs, then you need to modify your utility that xargs uses so it surpresses stderr. Then you can use the 2>&1 trick others have mentioned to move the command listing generated by xargs -t from stderr to stdout.

    % 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 collapses everything you want to stdout (leaving out what you don't want).

    If you find yourself doing this a lot, you can write a general utility to surpress 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
    %