Search code examples
bashbackground-process

Run Jobs Sequentially, in the Background, in Bash


I'm writing a bash script to execute a python program with different values of parameters 'H' and 'K'. What I would like to happen is that only one instance of 'program.py' runs at a time and subsequent jobs are not started until the previous one finishes.

Here's what I tried -

#!/bin/bash

H="1 10 100"

K="0.01 0.1"

for H_val in ${H}
do
        for K_val in ${K}
        do
               { nohup python program.py $H_val $K_val; } &
        done
done

However, this just loops over all the parameter values without waiting for any one job to finish. Conversely, if I modify the above slightly by taking off the ampersand, I can run each job individually - but not in the background. Any ideas on how to proceed?


Solution

  • Put the & on the command you want to put in the background. In this context, that might be your whole loop:

    #!/usr/bin/env bash
    #              ^^^^- ensures that array support is available
    
    hValues=( 1 10 100 ) # best practice is to iterate over array elements, not...
    kValues=( 0.01 0.1 ) # ...words in strings!
    
    {
      # perform conditional redirections akin to what nohup would do
      [ -t 0 ] && exec </dev/null      # stdin
      [ -t 1 ] && exec >myprogram.log  # stdout
      [ -t 2 ] && exec 2>&1            # stderr
    
      for hVal in "${hValues[@]}"; do
        for kVal in "${kValues[@]}"; do
          python program.py "$hVal" "$kVal"
        done
      done
    } &
    

    Notice how the & was moved to be after the done -- that way we background the entire loop, not a single command within it, so the backgrounded process -- running that loop -- invokes only one copy of the Python interpreter at a time.

    The redirections (</dev/null, >myprogram.log, and 2>&1) are equivalent to how nohup would redirect stdout and stderr to nohup.out if they weren't already going to a file (or other non-TTY destination) to ensure that they aren't attached to a TTY; adjust the name myprogram.log to your preference.