==================================== Avoiding deeply nested IF statements (Structured/Un-Structured) ==================================== -IAN! idallen@idallen.ca This file deals with when you might violate structured programming principles to make your code smaller and easier to read and modify. -------------------------------------- Deep nesting in structured programming -------------------------------------- Here is some typical deeply-nested IF code found in structured programming: if [ -e "$file" ] ; then if [ -r "$file" ] ; then if [ -w "$file" ] ; then if [ -x "$file" ] ; then if [ -s "$file" ] ; then echo "File '$file' is not empty and has RWX permissions" return=0 else echo 1>&2 "$0: File '$file' is empty (zero size)" return=1 fi else echo 1>&2 "$0: File '$file' has no X permissions" return=2 fi else echo 1>&2 "$0: File '$file' has no W permissions" return=3 fi else echo 1>&2 "$0: File '$file' has no R permissions" return=4 fi else echo 1>&2 "$0: File '$file' does not exist" return=5 fi exit $return From the point of view of structured programming, the above 28 lines of code are good because they have one entry point (the top of the file) and one exit point (the bottom of the file). However, the code is hard to read and hard to modify. To add, delete, or re-order an IF clause requires changing the indentation of all the other nested IF clauses. The multiple "else" parts of the IF statements are separated a long way from the IF, making the code difficult to understand. One might rework the code to move the error message clauses to the top; but, this still doesn't improve the nesting: if [ ! -e "$file" ] ; then echo 1>&2 "$0: File '$file' does not exist" return=5 else if [ ! -r "$file" ] ; then echo 1>&2 "$0: File '$file' has no R permissions" return=4 else if [ ! -w "$file" ] ; then echo 1>&2 "$0: File '$file' has no W permissions" return=3 else if [ ! -x "$file" ] ; then echo 1>&2 "$0: File '$file' has no X permissions" return=2 else if [ ! -s "$file" ] ; then echo 1>&2 "$0: File '$file' is empty (zero size)" return=1 else echo "File '$file' is not empty and has RWX permissions" return=0 fi fi fi fi fi exit $return Some relief from the deep indentation problems can be had by replacing "ELSE IF" by ELIF (not all programming languages have this feature!): if [ ! -e "$file" ] ; then echo 1>&2 "$0: File '$file' does not exist" return=5 elif [ ! -r "$file" ] ; then echo 1>&2 "$0: File '$file' has no R permissions" return=4 elif [ ! -w "$file" ] ; then echo 1>&2 "$0: File '$file' has no W permissions" return=3 elif [ ! -x "$file" ] ; then echo 1>&2 "$0: File '$file' has no X permissions" return=2 elif [ ! -s "$file" ] ; then echo 1>&2 "$0: File '$file' is empty (zero size)" return=1 else echo "File '$file' is not empty and has RWX permissions" return=0 fi exit $return The above structured code is only 20 lines - a 28% improvement over the original 28-line version; but, the chained list of negated IF/ELIF tests obscures the overall meaning of the code block. At least the re-ordering, adding, or deleting of IF clauses is easier than before; since, the indentation level is now constant. Less code is better code. ------------------------------------------------- Multiple exit points reduce need for deep nesting ------------------------------------------------- Here is code that produces the same output, without the deep nesting, and without the need to link the entire block of code together using ELIF (which is not available in some programming languages): if [ ! -e "$file" ] ; then echo 1>&2 "$0: File '$file' does not exist" exit 5 fi if [ ! -r "$file" ] ; then echo 1>&2 "$0: File '$file' has no R permissions" exit 4 fi if [ ! -w "$file" ] ; then echo 1>&2 "$0: File '$file' has no W permissions" exit 3 fi if [ ! -x "$file" ] ; then echo 1>&2 "$0: File '$file' has no X permissions" exit 2 fi if [ ! -s "$file" ] ; then echo 1>&2 "$0: File '$file' is empty (zero size)" exit 1 fi echo "File '$file' is not empty and has RWX permissions" exit 0 The above code is not good "structured programming", since the code has multiple exit points, not just one. However, the code is 20% shorter than the original example, easy to modify, and easy to read. You can add, modify, re-order, or delete IF clauses without changing any indentation. No "else" clauses are needed, since each IF clause simply exits the script. Both the structured and unstructured versions of this code are correct; both get you full marks. My preference is for code that is easy to read, understand, and modify, even if the code violates "structured programming" rules (one entry / one exit). Less code is better code. -------- Bad code -------- Here is code that looks similar to the structured programming example given earlier; but, you will note that this code has the worst of both worlds - it has multiple exit points *and* deep nesting: if [ -e "$file" ] ; then if [ -r "$file" ] ; then if [ -w "$file" ] ; then if [ -x "$file" ] ; then if [ -s "$file" ] ; then echo "File '$file' is not empty and has RWX permissions" exit 0 else echo 1>&2 "$0: File '$file' is empty (zero size)" exit 1 fi else echo 1>&2 "$0: File '$file' has no X permissions" exit 2 fi else echo 1>&2 "$0: File '$file' has no W permissions" exit 3 fi else echo 1>&2 "$0: File '$file' has no R permissions" exit 4 fi else echo 1>&2 "$0: File '$file' does not exist" exit 5 fi I would deduct marks for the above code. It does not adhere to structured programming rules; since, it has multiple exit points and not just one. Given that the code author is willing to abandon structured programming principles and use multiple exit points, s/he should have restructured the code to remove the indentation and the redundant "else" clauses and make the code smaller and easier to read and modify. The above code is too long for what it does; it is a large example of this common coding mistake: if ...something... ; then ...stuff1... exit 1 else ...stuff2... fi The above 6-line IF statement is equivalent to this 5-lines of code: if ...something... ; then ...stuff1... exit 1 fi ...stuff2... Adding an "else" clause after an IF clause that returns or exits is superfluous; the "else" is simply not needed. Don't do that. Less code is better code.