Tuesday, January 29, 2013

New year, new ideas (introduction to while/true case statements)

Howdy Autosnort users,

It's been a while since I've posted anything meaty to the blog. My day job keeps me very busy and the projects I've been working on have been very intense. At this point I really only have time to work on autosnort over weekends. I appreciate all the support and questions and identification of bugs or problems with the script; you're helping me, the snort community and the greater open source community just by asking questions and reporting bugs, so thank you very much. I may not be very fast to respond, but I will ALWAYS do what I can as soon as I can.

That being said, I'm going to post a quick tutorial to bash while/true loops, and how they can be used to provide some fault tolerance in your shell scripts (or at least how it's helped me tremendously)

First and foremost, what is a while true loop? A while true loop is a do while statement that is configured to loop infinitely. While do loops usually go something like this:

#!/bin/bash
x=0
while [ $x -le 10 ]; do

        echo "$x"
        x=$(( $x + 1 ))

done
exit 0
x is a variable initialized to zero in the example above. The while statement reads "while x is less than or equal to 10 do the function below.

the function below prints the value of x, then increments it by 1. Once x is greater than 10, the loop, then the script exits. This script will print numbers 0-10, one on each line.

A while true loop looks something like this:

#!/bin/bash
while true; do
       echo "Hello!"
done
 exit 0

this statement is much much simpler, but lets run through it anyhow. The do/while statement is just "while true; do" which, in english, means while true is true (which will ALWAYS be the case) do the statement below

The statement below just prints "Hello!" ad infinitum. If you were to run the script above, you'd have to kill the script or Ctrl+C the script to stop it. Not very useful, huh? What if I told you that you could control whether or not the loop progresses or not with certain keywords?

The keywords break and continue in a loop are ways to control how a bash loop progresses. The break statement says "Stop the loop now, jump immediately to done, do not process anymore lines in the loop." while the continue statement says "Stop here, go back to the beginning statement in the loop."

Are you starting to see how this can be useful?


Case statements can be used to give the use of a script a menu or "branch" a script, performing a certain action when a variable, or output is a certain value.

#!/bin/bash
read -p "this is a case statement!
Your choices are:
1
2
Please enter your choice: " case1

case $case1 in
      1)
             echo "you have chosen case statement 1!"
      ;;
      2)
             echo "you have chosen case statement 2!"
      ;;
      *)
             echo "invalid choice!"
      ;;
esac
exit 0

the above statement asks the user to input 1 or 2 as an option for the case statement. that value is stored in the variable case1. The case statement is basically a large collection of if/then statements -- "if the value of case1 equals 1, then do this. if it equals 2, then do this. if it equals anything else, this is a catch-all statement to do, usually indicating the user picked an invalid choice."

Nothing too mind-breaking. This is stuff you're taught in your intro to UNIX classes in college. The fun part comes combining the concept of a case statement with a while true loop. and using continue or break keywords to continue the loop or break it:


while true; do

        read -p "this is a case statement var choices are 1 or 2." case1
        case $case1 in
                1)
                        echo "this is a result of selecting 1."
                        break;
                ;;
                2)
                        echo "this is a result of selecting 2."
                        break;
                      
                ;;
                *)
                        echo "invalid choice!"
                        continue;
                ;;
        esac
done
exit 0

 Let me break down what's going on above:

We begin with a while true statement, starting off our "infinite" loop. The next statement tells the user to select 1 or 2 as options in our case statement. This is normally where you'd print a menu (if a user is expected to interact here) and explain what the options do, or just launch straight into your case selection if this is a fully automated script. After printing the statement and taking the user's input, we go straight into our case statement. The statement says "If case1 equals 1 do this, if it equals 2, then do this, if it equals anything else, do this."

What's different between this case statement and the previous one I used as an example is the existence of the keywords break and continue in these statements. The break statement says "Terminate the loop and jump straight to done." No more processing, no more looping, just move on. The break statement at the end of option 1 and 2  is a way of identifying that these are the only valid cases for this bit of bash code, that in order to continue script execution, the value of case1 must be "1" or "2" (or the user has to cancel the script via kill or ctrl+c). the continue statement in our catch-all case is a way if forcing the user to select a valid choice to continue script execution. In this way it's a sort of safety net that doesn't allow the script to continue unless a valid choice is made. It's a safety net for accidents, fat-fingering or hitting the wrong keys.

Soon as I figured out how to do this, I littered them throughout the script to make the script more fault tolerant. Instead of throwing an error and exiting in a case statement, autosnort just loops back and informs the user their choice was invalid, or didn't work for whatever reason, instead of blindly plowing forward, or exiting, leaving the user with a mess. It's still imperfect, but MUCH improved over the original version.

Currently I'm working on nesting multiple while/true case statements together, something like this:

 #!/bin/bash

while true; do

        read -p "this is a case statement var choices are 1 or 2." case1
        case $case1 in
                1)
                        echo "this is a result of selecting 1."
                        break;
                ;;
                2)
                        while true; do
                                read -p "this is another case statement. var choices are 1 or 2." case2
                                case $case2 in
                                        1)
                                                echo "result of the second case statement"
                                                break;
                                        ;;
                                        2)      echo "NARF!"
                                                break;
                                        ;;
                                        *)
                                                echo "Invalid choice!"
                                                continue;
                                        ;;
                                esac
                        done
                        break;
                ;;
                *)
                        echo "invalid choice!"
                        continue;
                ;;
        esac
done
exit 0

See if you can figure it out on your own. I hope to implement multiple output options (web UIs, etc.) in this manner to make the script more foolproof, and offer more choices to users.

Until next time,

Happy snorting!



No comments:

Post a Comment