0

I'm trying to make a basic loop, and I want to exit the nested loop when I loop the parent. The nested loop should restart each time the parent loops.

#!/bin/bash

loop1=0
i=0

while [[ $loop1 -eq "0" ]]; do

    let "i+=1"
    echo $i "--------------"

    loop2=0
    j=0

    while [[ $loop2 -eq "0" ]]; do

        let "j+=1"
        echo $j $i
        sleep .2    

    done &

    loop2=1
    sleep 2

done

My desired output would look like this:

1 --------------
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
9 1
10 1
2 --------------
1 2
2 2
3 2
4 2
5 2
6 2
7 2
8 2
9 2
10 2

I'm getting that output, sort of, but the original nested loop doesn't terminate, so I also get 11 1, 12 1, 13 1, and so on.

I'm sure this is dead simple, but I'm not getting it.

  • Why are you looping on $loop2 -eq 0 when it looks like you want $j -lt 11? – muru Jan 15 '18 at 4:06
  • It'll be an infinite loop, but I want to be able to terminate it when the parent loops. I was just using $loop2 -eq 0 in hopes that changing the value of the variable would end the loop. But that didn't really pan out. Obviously this script is a simplification. My $j won't necessarily be 10 – Adam Nerland Jan 15 '18 at 4:11
  • Your nested loop runs in a subshell, so no change in variables in the outer loop will affect it. You'll have use something else, like a file, to tell the inner loop that the outer loop has changed. – muru Jan 15 '18 at 4:12
  • The value of $j isn't really relevant to what I'm trying to do. I'm just trying to get the basic mechanics working. I want to have nested loops, and I want to be able to kill the nested loop and start it over when the parent loops. – Adam Nerland Jan 15 '18 at 4:13
  • I could use a file, but it seems a bit clunky. Is that the only option? I was also hoping "break" would help me, but it only works in the opposite scenario. (Killing the parent loop from within the nested loop.) One thing I noticed is that each "while" loop creates a new /bin/bash process with a unique PID. If there's a way to pinpoint the PID of the nested "while" loop, I could just kill the process at the end of the parent loop. But I couldn't figure out how to pinpoint it. – Adam Nerland Jan 15 '18 at 4:20
1

I'm not exactly sure what the practical use for this exercise is, but to kill background processes, I'd recommend saving the process id from $! and then just shooting it with kill.

The script element backgrounded with & runs in a subshell, a distinct shell process, so no changes to shell variables take any effect, in one direction or the other. (Usually this just comes up with changes in the child not showing in the parent, since usually we wait for the child process to complete.)

This:

#!/bin/bash

i=0
while true; do   # the loop runs forever, so just use 'true' here
    let "i+=1"
    echo $i "--------------"

    j=0
    while true; do   
        let "j+=1"
        echo $j $i
        sleep .2    
    done &

    pid=$!
    # disown   # remove the comment marker here
    sleep .5   # shorter timeout 
    kill $pid
done

prints:

1 --------------
1 1
2 1
3 1
2 --------------
1 2
2 2
3 2
loop.sh: line 19:  4113 Terminated              while true; do
    let "j+=1"; echo $j $i; sleep .2;
done
3 --------------
1 3
2 3
...

The shell prints out notices when it sees its child processes dying, we can get rid of that by disowning the child (remove the comment marker from the line).

0

Someone in the comments turned me onto the "jobs" command, and how to kill a process based on that. So this is what I ended up doing. Seemed the most straightforward.

i=0
while true; do
    let "i+=1"
    echo $i "--------------"

    j=0
    while true; do
        let "j+=1"
        echo $j $i
        sleep .2    
    done &

    sleep 2

    # jobs
    kill %"$i"
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.