0

A few questions about the sample script below.

I'm calling a function _foo and want to capture the output of it into a variable $bar, but also use the return status (which may not be 0 or 1), or failing that have exit stop the script (when non-zero).

  • Why doesn't the exit in function _foo work when called this way? (if ! bar="$(_foo)"). It works when called "normally".
    • the exit will stop the script if I change the if statement to this (but I lose its output): if ! _foo ; then
    • the exit behaves like return and will not stop the script: if ! bar="$(_foo)" ; then
    • Just calling a function without the assignment and an exit will work, however calling it like var="$(func)" doesn't.
  • Is there a better way to capture the output of _foo into $bar from the function as well as use return status (for other than 0 or 1, eg a case statement?)

I have a feeling I may need to use trap somehow.


Here's a simple example:

#!/usr/bin/env bash

set -e
set -u
set -o pipefail

_foo() {
    local _retval
    echo "baz" && false
    _retval=$?
    exit ${_retval}
}

echo "start"

if ! bar="$(_foo)" ; then
    echo "foo failed"
else
    echo "foo passed"
fi

echo "${bar}"
echo "end"

Here's the ooutput:

$ ./foo.sh 
start
foo failed
baz
end

Here's some more examples:

This will exit:

#!/usr/bin/env bash

set -e
set -u
set -o pipefail

func() {
    echo "func"
    exit
}

var=''
func
echo "var is ${var}"
echo "did not exit"

This will not exit:

#!/usr/bin/env bash

set -e
set -u
set -o pipefail

func() {
    echo "func"
    exit
}

var=''
var="$(func)"
echo "var is ${var}"
echo "did not exit"
3

exit within a function exits the entire script and not just the function (subshells notwithstanding). To expound:

#!/bin/bash
f() {
   exit 3
}
f
exit 0

The above script will terminate with exit code 3, while

#!/bin/bash
f() {
   exit 3
}
(f)
exit 0

will terminate with exit code 0.

The $(command) syntax you are using runs command within a subshell, and exit can only break out as far as the layer that subshell is running within.

If you want to capture the exit code and output of something run within a subshell, that is still available to the environment in which the subshell is initiated:

#!/bin/bash
subshelloutput="$( echo "output"; exit 3 )"
returnval=$?  # captures subshell's exit code
: more stuff follows
  • Thank you, that makes sense. Is there a way to capture the exit 3 outside of a subshell? I want both the STDOUT and the RETVAL. I guess with $?. – David Dec 7 '16 at 21:38
  • See expanded answer. – DopeGhoti Dec 7 '16 at 21:42
  • Thank you. The set -e I had was causing it to exit prematurely in the example you gave, but I understand how it's behaving now. For anyone else who ends up here, this link has some good examples of subshells and return statuses: mywiki.wooledge.org/BashFAQ/002 – David Dec 7 '16 at 21:56
  • set -e will terminate a script if anything returns a nonzero exit code that isn't immediately caught with an if or other conditional statement. – DopeGhoti Dec 7 '16 at 22:04
0

You can also do it another way.

_foo () 
{ 
    local _retval
    declare -n output="$1"     # $output is local (by default with declare in a func.)
                               ## and points to $1
    output="baz" && false
    _retval=$?
    echo ${_retval}            # change echo to exit
}

Now call _foo this way:

$ _foo bar
1
$ echo "$bar"
baz

When you change echo to exit in the function definition, the exit will exit the script.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.