Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best practice to redirect all original (only) stderr outputs to /dev/null in a bash function?

Tags:

bash

Is there any best practice to:

  1. Not have to follow each command with 2> /dev/null
  2. Not lose own >&2 outputs of the function
  3. Have a simple flag allowing to temporarily disable (1) above, or to custom collect it in e.g. logfile

IMPORTANT Only the ORIGINAL stderr output is meant to be suppressed/redirected, NOT ALL.

It is quite tedious to be redirecting every single command's error output to /dev/null, it is much easier to redirect a { ...; } block, or a function. But it's impossible then to have that function produce own >&2 outputs - unless sent to stdout instead, which is undesirable.

Example:

func() {
    ping 1.1.1.1 2>/dev/null || echo "DNS unreachable." >&2

    [[ -n $GVAR ]] || echo "Environment not set." >&2

    stat /tmp/a_secret_file 2>/dev/null || echo "Cannot stat secret file." >&2
}

Whatever produced by ping and stat should be suppressed, but own echo messages shown.

like image 859
Albert Camu Avatar asked Dec 21 '25 06:12

Albert Camu


2 Answers

It's pretty unusual to want to discard all stderr from a command as it usually contains useful information that makes it easier to debug any problem on a failure. Discarding stderr from a block of multiple commands would be even more unusual. You might be better off saving the stderr in a variable so you can print it if the command that produced it exited with a failure, e.g.:

$ cat tst.sh
#!/usr/bin/env bash

foo() { echo 'foo entered' >&4; echo 'foo succ' >&2; return 0; }
bar() { echo 'bar entered' >&4; echo 'bar fail' >&2; return 9; }

func1() { foo || bar; }
func2() { foo && bar; }

exec 3>&1 4>&2 #set up extra file descriptors

# em=Error Message, es=Exit Status

em=$( func1 2>&1 1>&3 ) ||
    { es="$?"; printf 'func1 exited with status "%s", stderr:\n%s\n' "$es" "$em" >&2; }

em=$( func2 2>&1 1>&3 ) ||
    { es="$?"; printf 'func2 exited with status "%s", stderr:\n%s\n' "$es" "$em" >&2; }

exec 3>&- 4>&- # release the extra file descriptors

$ ./tst.sh
foo entered
foo entered
bar entered
func2 exited with status "9", stderr:
foo succ
bar fail

Notice above that you only see the stderr output from foo and bar when you want to see it, i.e. when bar is called from func2 thereby causing func2 to fail. You can obviously instrument your code with those file descriptor redirections just at the highest level (func1 and func2 in my example) without making any changes to the functions or commands it calls (foo and bar in my example), or you could write a wrapper to do that work.

The redirections to &4 are to show how you can add your own echo statements that always go to stderr and bypass the above "only on a failure" logic.

The down side, of course, is that you need to call that highest level function in a subshell but that may be a worthwhile tradeoff for you.

The redirection logic above is largely borrowed from @DennisWilliamsons and @Tinos answers at How to store standard error in a variable see those and other answers to that question (or links from there to yet more similar posts) for more information on saving stderr to a variable (or file). See also the FAQ How can I store the return value and/or output of a command in a variable?.

like image 104
Ed Morton Avatar answered Dec 23 '25 23:12

Ed Morton


You can redirect the whole function, but use a different file handle for its "own" standard error.

An example:

#!/bin/bash
func() {
    ping 1.1.1.1 || echo DNS unreachable >&3
    ...
} 3>&2 2>/dev/null

func

To change the behaviour, you can have two different functions wrapping the actual one with different redirections:

#!/bin/bash
func() {
    echo This goes nowhere >&2
    echo This goes to stderr >&3
}

f1 () {
    func
} 3>&2 2>/dev/null

f2 () {
    func
} 3>&2

echo f1
f1

echo f2
f2  # The message "This goes nowhere" appears on stderr.
like image 42
choroba Avatar answered Dec 23 '25 23:12

choroba



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!