nino
nino

Reputation: 678

unable to exit a function in a sourced file

I have file1:

#!/usr/bin/env bash
source $HOME/file2

answer=$(calculate)
echo "Answer is: $answer"

and file2:

#!/usr/bin/env bash

function calculate(){
    local result=$(echo $((RANDOM%2)))
    [ $result -gt 0 ] && exit 1
    echo "$result"  ## this line was missed and was just added
}

The problem is calculate function never exits, i.e. the line Answer is: gets printed anyway whatever the function output is.
Inserting set -u or set -eu at the top of both or either of the files was not helpful either.
I have also tried to make it exit using return 1, but still no luck.

Upvotes: 0

Views: 56

Answers (2)

Mark
Mark

Reputation: 4453

Here's a solution that uses exit codes, but doesn't call exit.

File1:

#!/usr/bin/env bash
source $HOME/file2

calculate
echo "Answer is: $?"

Please note the substantial difference here - I am calling calculate directly. I am ignoring stdout from calculate but rather just capturing the exit status: $?.

And File2:

#!/usr/bin/env bash

function calculate(){
    local result=$(echo $((RANDOM%2)))
     [ $result -gt 0 ] 
}

In file2, the status returned from calculate is the status of the last command executed. Test ([]) returns 0 for true and 1 for false. So if $result is greater than 0, a 0 will be returned (which is the opposite of what you want). Come to think of it, here's something more like what you were looking for:

#!/usr/bin/env bash

function calculate(){
    local result=$(echo $((RANDOM%2)))
     ! [ $result -gt 0 ] 
}

The big change in calculate() is that the exit code from the last command executed is being used to produce the exit status of the function. If you use exit in a function you call directly, the exit will terminate your calling shell immediately.

Upvotes: 0

chepner
chepner

Reputation: 532208

If you want to capture the output of the function, it necessarily has to run in a subshell, which means you'll have to explicitly check the exit status from the caller anyway, so you may as well just return a non-zero value.

calculate () {
    local result=$(( RANDOM % 2 ))
    [ "$result" -gt 0 ] || return 1
    echo "$result"
}

answer=$(calculate) || exit 1

The exit status of this assignment is the exit status of the command substitution. (And strictly speaking, if you don't use local, then result will also be defined as a global variable in the subshell of the command substitution, not in your script's environment.)


Alternatively, you can let calculate set a global variable instead of writing anything to standard output.

calculate () {
    answer=$(( RANDOM % 2 ))
    [ "$answer" -gt 0 ] || exit 1
}

calculate
echo "Answer = $answer"

Upvotes: 2

Related Questions