Search code examples
bashshellwhile-loopcontinue

Skip bash while loop if function inside fails a condition


I have a while loop that reads a file line by line and pass the line to multiple functions or nested functions inside the loop. if one of the function finds something wrong I want the while loop to skip that iteration and go to the next one.

I did search a lot and tried different things but using 'continue to skip the while loop was the only solution everywhere but it doesn't seem to be helping. I am not sure where or how to look for something like this. Is there a solution or a different approach to solving this issue? Thanks for any help.

   function2(){
   "if some condition that uses $test_name fails, skip the while loop"
  }

  function3(){
   do something
  }

  main_function (){
    do something to $test_name
    function2 $test_name
    function3 $test_name
  }

  while true read -r line; do
    if [[ ! "${line}" =~ ^# && ! -z "${line}" ]]; then
       test_name=$line
        main_function  $test_name 
    fi
 done < $OS_LIST

Solution

  • First, write the functions so that they return a nonzero status if they fail, zero if they succeed (actually, you should be doing this anyway as a general good practice). Something like this:

    function2() {
        if some condition that uses $test_name fails; then
            echo "test condition failed in function2" >&2    # Error messages should be sent to stderr
            return 1
        fi
        # Code here will only be executed if the test succeeded
        do_something || return 1
        # Code here will only be executed if the test AND do_something both succeeded
        do_something_optional    # No error check here means it'll continue even if this fails
        do_something_else || {
            echo "do_something_else failed in function2" >&2
            return 1
        }
        return 0    # This is actually optional. By default it'll return the status
                    # of the last command in the function, which must've succeeded
    }
    

    Note that you can mix styles here (if vs || vs whatever) as the situation warrants. In general, use the style that's clearest, since your biggest enemy is confusion about what the code's doing.

    Then, in the main function, you can check each sub-function's exit status and return early if any of them fail:

    main_function (){
        do something to "$test_name" || return 1    # BTW, you should double-quote variable references
        function2 "$test_name" || return 2    # optional: you can use different statuses for different problems
        function3 "$test_name"  || return 1
    }
    

    If you need to skip the end of the main loop, that's where you'd use continue:

    while true read -r line; do
        if [[ ! "${line}" =~ ^# && ! -z "${line}" ]]; then
            test_name=$line
            main_function "$test_name" || continue
            echo "Finished processing: $line" >&2    # Non-error status messages also go to stderr
        fi
    done < "$OS_LIST"