How to act on an error code and collect stdout at the same time?

Viewed 32

I have this python3 program. It sets error code 0 when it works and non-zero when it fails. It puts the good message into stdout and the failure string into stderr. I think it's well-behaved.

#! /bin/python3

import sys

def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

cs = sys.argv[1]

if str(cs) == "1":
    print("GOOD")
else:
    eprint("BAD")
    sys.exit(1)

See how I test it:

$ X=`python3 example.py 1`; echo It is: $X
It is: GOOD
$ X=`python3 example.py 0`; echo It is: $X
BAD
It is:

I have this bash program that calls the python program:

#! /bin/bash

function do_example() {
    local DB=$1
    local URI=`python3 example.py $DB`
    RESULT=$?
    if [ $RESULT -eq 0 ]; then
        echo $URI
        echo success
    else
        echo $URI
        echo failed
    fi
}

do_example 1
do_example 0

It runs a python program first. Captures the stdout. Then it checks the return value. It doesn't work. It should say success and then failed. Instead, it says success both times. It doesn't work. It doesn't handle the error code correctly.

$ ./example.sh 
success
BAD
success
$ ./example.sh 
GOOD
success
BAD

The generalized question is how to collect the stdout into a local variable and act on the error code at the same time?

2 Answers

local's exit code overrides the one from the command substitution. To work around this, always split apart local declarations from assignments if the assignments could fail:

local URI
URI=`python3 example.py $DB`
RESULT=$?

By the way, you don't need to save the exit code in a variable if you check it in the if statement. Also, it's best to avoid uppercase variable names as they could clash with built-in names. Finally, I recommend using $(...) in place of backticks; it is easier to nest substitutions.

local db=$1

local uri
if uri=$(python3 example.py "$db"); then
    echo "$uri"
    echo success
else
    echo failed
fu

Don't bother checking $? explicitly. Just do:

#! /bin/bash

do_example() {
    local DB=$1
    local URI 
    if URI=$(python3 example.py $DB); then    
        echo "$URI"
        echo success
    else
        echo "$URI"
        echo failed
    fi
}

do_example 1
do_example 0
Related