How to store standard error in a variable

后端 未结 18 2143
难免孤独
难免孤独 2020-11-22 12:46

Let\'s say I have a script like the following:

useless.sh

echo \"This Is Error\" 1>&2
echo \"This Is Output\" 

And I have an

相关标签:
18条回答
  • 2020-11-22 13:27
    # command receives its input from stdin.
    # command sends its output to stdout.
    exec 3>&1
    stderr="$(command </dev/stdin 2>&1 1>&3)"
    exitcode="${?}"
    echo "STDERR: $stderr"
    exit ${exitcode}
    
    0 讨论(0)
  • 2020-11-22 13:29

    If you want to bypass the use of a temporary file you may be able to use process substitution. I haven't quite gotten it to work yet. This was my first attempt:

    $ .useless.sh 2> >( ERROR=$(<) )
    -bash: command substitution: line 42: syntax error near unexpected token `)'
    -bash: command substitution: line 42: `<)'
    

    Then I tried

    $ ./useless.sh 2> >( ERROR=$( cat <() )  )
    This Is Output
    $ echo $ERROR   # $ERROR is empty
    

    However

    $ ./useless.sh 2> >( cat <() > asdf.txt )
    This Is Output
    $ cat asdf.txt
    This Is Error
    

    So the process substitution is doing generally the right thing... unfortunately, whenever I wrap STDIN inside >( ) with something in $() in an attempt to capture that to a variable, I lose the contents of $(). I think that this is because $() launches a sub process which no longer has access to the file descriptor in /dev/fd which is owned by the parent process.

    Process substitution has bought me the ability to work with a data stream which is no longer in STDERR, unfortunately I don't seem to be able to manipulate it the way that I want.

    0 讨论(0)
  • 2020-11-22 13:30

    This is an interesting problem to which I hoped there was an elegant solution. Sadly, I end up with a solution similar to Mr. Leffler, but I'll add that you can call useless from inside a Bash function for improved readability:

    #!/bin/bash
    
    function useless {
        /tmp/useless.sh | sed 's/Output/Useless/'
    }
    
    ERROR=$(useless)
    echo $ERROR
    

    All other kind of output redirection must be backed by a temporary file.

    0 讨论(0)
  • 2020-11-22 13:30

    In zsh:

    { . ./useless.sh > /dev/tty } 2>&1 | read ERROR
    $ echo $ERROR
    ( your message )
    
    0 讨论(0)
  • 2020-11-22 13:32

    A simple solution

    { ERROR=$(./useless.sh 2>&1 1>&$out); } {out}>&1
    echo "-"
    echo $ERROR
    

    Will produce:

    This Is Output
    -
    This Is Error
    
    0 讨论(0)
  • 2020-11-22 13:34

    POSIX

    STDERR can be captured with some redirection magic:

    $ { error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&3 ; } 2>&1); } 3>&1
    lrwxrwxrwx 1 rZZt rZZt 7 Aug 22 15:44 /bin -> usr/bin/
    
    $ echo $error
    ls: cannot access '/XXXX': No such file or directory
    

    Note that piping of STDOUT of the command (here ls) is done inside the innermost { }. If you're executing a simple command (eg, not a pipe), you could remove these inner braces.

    You can't pipe outside the command as piping makes a subshell in bash and zsh, and the assignment to the variable in the subshell wouldn't be available to the current shell.

    bash

    In bash, it would be better not to assume that file descriptor 3 is unused:

    { error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1; 
    exec {tmp}>&-  # With this syntax the FD stays open
    

    Note that this doesn't work in zsh.


    Thanks to this answer for the general idea.

    0 讨论(0)
提交回复
热议问题