Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a shell script with nested loops and just found out that "exit" doesn't really exit the script, but only the current loop. Is there another way to completely exit the script on a certain error condition?

I don't want to use "set -e", because there are acceptable errors and it would require too much rewriting.

Right now, I am using kill to manually kill the process, but it seems there should be a better way to do this.

share|improve this question
1  
What do you mean that "exit" doesn't really exit the script? It does, just try bash -c 'for x in y z; do exit; done; echo "This never gets printed"'. –  Chris Down 18 hours ago
    
You're right, it normally should exit out of nested loops, but when I use exit my script continues with the outer loop. I can't post the script. –  user923487 17 hours ago
2  
Why can't you write a script that shows the problem and post it here? That sounds unlikely to me. –  Toby Speight 17 hours ago
1  
Is it the case that the inner loop takes place in a sub-shell in your code? –  Toby Speight 17 hours ago
    
@Toby Most of the script is in a sub shell for logging purposes, but both loops and the rest of the code are in the same sub shell. –  user923487 17 hours ago

3 Answers 3

Your problem is not nested loops, per se. It's that one or more of your inner loops is running in a subshell.

This works:

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        for j in $(seq 1 10) ; do
                echo j $j
                sleep 1
                [[ $j = 3 ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        echo "After the j loop."
done
echo "After all the loops."

output:

i 1
j 1
j 2
j 3
I've had enough!

This presents the problem you have described:

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        cat /etc/passwd | while read line; do
                echo LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        echo "After the j loop."
done    
echo "After all the loops."

output:

i 1
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
After the j loop.
i 2
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
After the j loop.
i 3
LINE root:x:0:0:root:/root:/bin/bash
(...etc...)

Here is the solution; you have to test the return value of inner loops that run in subshells:

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        cat /etc/passwd | while read line; do
                echo LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        [[ $? != 0 ]] && exit $?
        echo "After the j loop."
done
echo "After all the loops."

Note the test: [[ $? != 0 ]] && exit $?

output:

i 1
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!

The variables "i" and "j" brought to you courtesy of Fortran. Have a nice day. :-)

share|improve this answer

You can use break.

From help break:

Exit a FOR, WHILE or UNTIL loop.  If N is specified, break N enclosing loops.

So for exiting from three enclosing loops i.e. if you have two nested loops inside main one, use this to exit from all of them:

break 3
share|improve this answer
    
There's more code after the loops, so breaking out of those alone is not enough. –  user923487 18 hours ago

exit does terminate the whole shell, or the current sub-shell:

$ bash -c 'for i in 1 2 3; do for j in 4 5 6; do echo $i; exit 1; echo $j; done; done'
1
$ echo $?
1
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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