Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash. Return two function levels (two nested calls)

I need to know if Bash has some solution for my case. I need after some conditions to do a "double return". I mean, to perform a return of a function and also return the parent function to skip the rest of the code of that parent function.

I know that I can do a conditional using function return values to achieve this. But I'd like to know if in Bash exist something like "break 2" for functions. I don't want if possible to modify the code of the parent function because as you can imagine, in my real script there are dozens of functions and I don't want to modify all of them.

Example:

#!/bin/bash

function sublevelone() {
    echo "sublevelone"
    # Return 2, or break 2 or something :D
}

function main() {
    sublevelone
    echo "This is the part of the code to being avoid executed"
}

main
like image 418
OscarAkaElvis Avatar asked Apr 29 '26 06:04

OscarAkaElvis


2 Answers

I don't know what the bash experts will think, but this works at least for simple cases:

multireturn(){
    [ -n "$1" ] && poplevel="$1"
    if [ "$poplevel" -ge 0 ]; then
        trap multireturn DEBUG
        shopt -s extdebug
        (( poplevel-- ))
        return 2
    else
        shopt -u extdebug
        trap - DEBUG
        return 0
    fi
}

This makes use of the DEBUG trap and the extdebug flag:

trap [-lp] [[arg] sigspec ...]
     [...]             If a sigspec is DEBUG, the command arg is exe‐
     cuted before every simple command, for  command,  case  command,
     select  command,  every  arithmetic  for command, and before the
     first command executes in a shell function [...]
shopt [-pqsu] [-o] [optname ...]
     [...]
     extdebug
             If  set at shell invocation, or in a shell startup file,
             arrange to execute the debugger profile before the shell
             starts,  identical to the --debugger option.  If set af‐
             ter invocation, behavior intended for use  by  debuggers
             is enabled:
             [...]
             2.     If  the  command  run by the DEBUG trap returns a
                    non-zero value, the next command is  skipped  and
                    not executed.
             3.     If  the  command  run by the DEBUG trap returns a
                    value of 2, and the shell is executing in a  sub‐
                    routine  (a shell function or a shell script exe‐
                    cuted by the . or  source  builtins),  the  shell
                    simulates a call to return.
             [...]
             5.     Function tracing is  enabled:  command  substitu‐
                    tion, shell functions, and subshells invoked with
                    ( command ) inherit the DEBUG and RETURN traps.

Example usage:

#!/bin/bash

multireturn(){
    [ -n "$1" ] && poplevel="$1"
    if [ "$poplevel" -ge 0 ]; then
        trap multireturn DEBUG
        shopt -s extdebug
        (( poplevel-- ))
        return 2
    else
        shopt -u extdebug
        trap - DEBUG
        return 0
    fi
}

# define 8 levels of function calls
# (level N prints output, calls level N+1, then prints more output)
for i in $(seq 1 8); do
    eval \
'level'$i'(){
    echo -n " '$i'"
    level'$((i+1))'
    echo -n "('$i')"
}'
done

# final level calls multireturn
level9(){
    echo -n " 9"
    multireturn $n
    echo -n "(9)"
}

# test various skip amounts
for i in $(seq 0 10); do
    echo -n "$i:"
    n=$i
    level1
    echo .
done

echo
echo done

Result:

0: 1 2 3 4 5 6 7 8 9(9)(8)(7)(6)(5)(4)(3)(2)(1).
1: 1 2 3 4 5 6 7 8 9(8)(7)(6)(5)(4)(3)(2)(1).
2: 1 2 3 4 5 6 7 8 9(7)(6)(5)(4)(3)(2)(1).
3: 1 2 3 4 5 6 7 8 9(6)(5)(4)(3)(2)(1).
4: 1 2 3 4 5 6 7 8 9(5)(4)(3)(2)(1).
5: 1 2 3 4 5 6 7 8 9(4)(3)(2)(1).
6: 1 2 3 4 5 6 7 8 9(3)(2)(1).
7: 1 2 3 4 5 6 7 8 9(2)(1).
8: 1 2 3 4 5 6 7 8 9(1).
9: 1 2 3 4 5 6 7 8 9.
10: 1 2 3 4 5 6 7 8 9
done

Note that every function that will be short-circuited should contain at least one additional command after the call to multireturn (or the function that invoked it). If it doesn't, the DEBUG trap will be invoked in the wrong context and an extra level will be skipped. For example, compare what happens above if either the echo -n "('$i')" or echo -n "(9)" line is deleted.

Note also that multireturn is non-reentrant.

like image 137
jhnc Avatar answered May 01 '26 19:05

jhnc


This is kinda whacky but if you use parentheses to define levelone, it will execute the function in a subshell and then you can exit out of that shell from an inner function. That said, I think it's more appropriate to use return to send back value that you check for in the parent function.

#!/bin/bash

function leveltwo() {
        echo "two"
        exit
}

function levelone() (
        echo "one"
        leveltwo
        echo "three"
)

levelone
echo "four"

Will print:

one
two
four

like image 24
Neil Avatar answered May 01 '26 18:05

Neil



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!