3

My Bash script looks roughly like this:

#!/bin/bash
function a { echo -n "A is running..."; sleep 5; echo "done (A)" }
function b { echo -n "B is running..."; sleep 1; echo "done (B)" }
function c { echo -n "C is running..."; sleep 3; echo "done (C)" }

a
b
c

Because all three operations are not the fastest in the universe I was thinking about speeding things up by running things in parallel, like so:

a & b & c & wait

However, that messes up the output to something like this:

[1] 21405
A is running...[2] 21406
B is running...[3] 21408
C is running...done (B)
[2]  - 21406 done       b
done (A)
[1]  - 21405 done       a
done (C)
[3]  + 21408 done       c

Ideally, I'd like the output to be similar to that of the original job with the processes in a fixed order so the user can see what's currently running:

A is running...done (A)
B is running...done (B)
C is running...done (C)

This means that when B finishes I'd like something like this:

A is running...
B is running...done (B)
C is running...

Is that possible? What's a good way to achieve that without rewriting too much of the functions (preferably)?

And on a related note, is it possible to add a circuit breaker, i.e. if, say, B fails, A and C are automatically terminated?

Max Power
  • 952
  • 9
  • 24
  • 4
    See **GNU Parallel** Here for tagging output https://stackoverflow.com/a/57872011/2836621 and here for stop on fail https://stackoverflow.com/a/43187508/2836621 – Mark Setchell Oct 17 '19 at 08:39
  • 1
    ... and here for making bash functions known to **GNU Parallel** with `export -f functioname` https://stackoverflow.com/a/57708517/2836621 – Mark Setchell Oct 17 '19 at 08:44
  • Consider adding `gnu-parallel` tag to your question to attract better suggestions from experts. – Mark Setchell Oct 17 '19 at 08:54
  • I had heard about GNU parallel but I had no idea it was this awesome! Thanks. – Max Power Oct 17 '19 at 08:58
  • If you really want to do it yourself capture each file's output to a temp file or variable, then print them all at the end. You could use wait -n to print each output as the job finishes. – Gem Taylor Oct 17 '19 at 10:08
  • Maybe I'm not understanding it but after exporting the functions and trying to run `parallel ::: a b c` it just waits forever. – Max Power Oct 17 '19 at 11:10

1 Answers1

0
#!/bin/bash                                                                     
function a { sleep 8; }                                                         
function b { sleep 10; }                                                        
function c { sleep 5; }                                                         

a &                                                                             
b &                                                                             
c &                                                                             

echo 'a is running.'                                                            
echo 'b is running.'                                                            
echo 'c is running.'                                                            

while true; do                                                                  
        wait -n                                                                 

        A=`jobs -l | cut -b2`                                                   


        echo -n -e "\e[3A"                                                      
        if [[ $A =~ 1 ]]; then                                                  
                echo 'a is running.'                                            
        else                                                                    
                echo 'a is running... (done).'                                  
        fi                                                                      
        if [[ $A =~ 2 ]]; then                                                  
                echo 'b is running.'                                            
        else                                                                    
                echo 'b is running... (done).'                                  
        fi                                                                      
        if [[ $A =~ 3 ]]; then                                                  
                echo 'c is running.'                                            
        else                                                                    
                echo 'c is running... (done).'                                  
        fi                                                                      

        if [ -z "$A" ]; then break; fi                                          
done

Note that command echo -n -e "\e[3A" move cursor 3 lines up.

Robin Hsu
  • 174
  • 1
  • 9