Published: 12. 6. 2024   Category: Programming

How to restart for-cycle iteration in bash

Programming languages usually offer statements like break and continue inside for-cycles to stop the cycle with a break or start the next iteration with continue.

There are some situations when you need to repeat and iteration. In the shell, it can be done with an additional loop inside the for-cycle which is explicitly testing if the main_task will finish properly or not (based on its return value [in shell, 0 true or 1/non-zero false]).

The basic stump of the for-cycle with embedded repeat loop is here:

result=1 # initialized as failed
for i in $DATA_LEN
do
    while [ $result -ne 0 ] ; do
        # Main task
        main_task $i
        result=$?
    done
done

In my case, I needed to export some data from an unstable API which was randomly failing because the amount of data processed in the backend caused some memory leaks. Later it was fixed by developers, but in the meantime, I have brought this solution: bash_loop_v1.sh. The main reason was, that the script was running as a cron job in Nomad orchestration and there is no time-out for the cron job (only check for overlaps).

The script for testing contains dummy_function which just waits some time and returns an error randomly. There is a check for elapsed time inside the embedded while-loop and if the total execution time is more than $TIME_OUT the script will exit. I was lucky, that this solution was working because the main task always exited after some time. However, if the last iteration takes some time, it will not exit exactly when the time out is set, but sometime after, when the condition is checked.

The previous issue is solved in bash_loop_v2.sh. The check for time out is done by a co-process wait_kill. At the beginning, the wait_kill will sleep in the background and if sleep ends it will send a TERM signal to the parent process and terminate it. In case, that for-cycle will finish faster it will terminate the wait_kill during its sleep.