Search code examples
arraysbashshellexit-code

How to create a "resume structure" based on shell functions return value


Let say, i have 3 functions as below:

#!/bin/bash

f_1()
{
if [ -d "/home/xxx/yyy" ];
   then
       return 0
   else
      return 1
fi
}

f_2()
{
if [ -d "/home/xxx/yyy" ];
   then
       return 0
   else
      return 1
fi
}

f_3()
{
if [ -d "/home/xxx/yyy" ];
   then
       return 0
   else
      return 1
fi
}

I want to construct a way to do the below:

excute function 1 ....if return is 0 continue with ....excute function 2....and so one, else (return 1) stop the loop.

Also i want to "resume", at the next execution starting with the function that failed.(returned 1 )


Solution

  • For a "stateful" application that remembers anything from the last run, you need somewhere to store the last state. The easiest solution is a filw, so we'll use that. The file will be called "next_command", but basically we can use any other file name as well.

    Next, we need the "conditional or" statement. It takes the form of a || b and is evaluated lazily. This means: If the first command is successful, the entire statement of a || b is already true, so later statements are not executed anymore. We can use this to only execute the second command if the first failed - it's somewhat shorter than an "if ... ; then ... fi"-construct, though that would of course also work.

    And last but not least we will use the case-construct to jump to the correct part of the code when we load a previously saved state.

    The following code does what you request:

    # ...
    # your functions here
    # ...
    
    save_state() {
        echo "$1" > next_command
    }
    
    load_state() {
        cat next_command
    }
    
    # this means: save the output of load_state in the var state
    # use f_1 as default)
    state="$(load_state)"
    
    # this means: take the content ofd $state, use "f_1" as default if it's empty
    # and then, compare it's content to each pattern noted below. For the first that 
    # matches, execute all code until you encounter ;;
    case "${state:-f_1}" in 
    
        f_1)
            f_1 || {
                save_state f_1
                exit 1
            }
            ;& # this is a fall-through - it means we continue executing code in the following cases
    
        f_2)
            f_2 || {
                save_state f_2
                exit 1
            }
            ;&
    
        f_3)
            f_3 || {
                save_state f_3
                exit 1
            }
            ;; # here, we do not want to continue but instead go to the end of the case
    
        *) # default, if sate is something invalid
            echo "Invalid saved state, terminating execution"
            exit 2
    esac
    
    # we went through everything, clear last saved state
    save_state ""