Flow control is the last foundational tool that we need to cover. Testing for and acting on given conditions and repeating actions are fundamental to programming. These chapters introduce conditional execution with if statements and repetition with while and for loops.
Edit me

Flow Control: Branching with if

Below is a graphical representation of the logic behind an if statement.

Diagram of if and if-else statements

if statements are used throughout programming for flow control–making decisions and controlling how the program runs. If some condition is met, do one thing, otherwise (else) do something else. if statements are used to make choices among multiple conditions. Once one condition is met, that portion of the code is executed, so it is important to think about the order of ‘else if’ statements. In Bash and Python, ‘else if’ is squished together to make the elif statement. else, if used, is always the last statement because if the program makes it through all the if and elif statements without any of them being true, else is used to handle any other condition–there is no test, it is automatically true.

  • p. 393: if: Type (not copy/paste) the code in the box at the bottom of 393 on the command line, hitting enter at the end of the lines:

    [magitz@login3 ~]$ if [ "$x" -eq 5 ]; then
    >   echo "x equals 5."
    > else
    >   echo "x does not equal 5."
    > fi
    x equals 5.
    

    Notice that after the first line, the prompt changes to a >. This is Bash’s way of saying it is expecting more information to complete the command–you haven’t given it all the information that it needs yet. In this case, we have said we want to setup a conditional execution. if x is equal to 5…but we haven’t told Bash what to do. So Bash gives the > prompt to ask for more commands.

  • p. 394: if: In the box on the next page, the text shows the same command typed out on one line using the ; to separate each part of the command instead of new lines. Both work just fine. Also, rather than re-type the command, remember about the -arrow to bring back commands from your history. Also note that even though we typed the command on multiple lines, when we recall it from the history, it is on a single line.

  • p. 394: if statement syntax: We discussed the format of documentation before, but here is another good example:

    if commands; then
    commands
    [elif commands; then
    commands...]
    [else
    commands]
    fi
    

    An if statement must start with if, followed by some commands (a conditional statement). Those commands have to be followed by either a ; or a new line and the word then. That must be followed by what to do when the commands result in a true statement. Optionally (they are in square brackets, so interpreted as optional), we can add a second set of commands with the elif. The elif can be repeated (‘…’). Optionally, that is followed by the else (without commands or then). The entire if statement must end in fi (“if spelled backward–we will see this is a convention Bash uses frequently).

    The concept and structure of an if statement is common to all programming languages that I know. The syntax changes from language to language, but the concept is the same. For example, how a language bounds the parts of a statement varies. Bash uses if, then, fi; other languages use parentheses, curly-braces, or square-brackets; Python uses indentation. Regardless of syntax, the concept is the same and important to understand.

  • p. 396: test: As above, it is important to note that the [ expression ] format has a single space on either side of the expression. See what happens if I delete the spaces on this example:

      [magitz@login2 magitz]$ FILE=~/.bashrc
      [magitz@login2 magitz]$ if [ -f "$FILE" ]; then echo "$FILE is a regular file."; fi
      /home/magitz/.bashrc is a regular file.
      [magitz@login2 magitz]$ if [-f "$FILE"]; then echo "$FILE is a regular file."; fi
      -bash: [-f: command not found
      [magitz@login2 magitz]$
    
  • p. 400: Integer expressions: note that these really do only work with integers (whole numbers with no decimal):

      [magitz@login2 magitz]$ x=5.4
      [magitz@login2 magitz]$ y=6.7
      [magitz@login2 magitz]$ if [ $x -lt $y ]
      > then
      > echo "x less than y"
      > fi
      -bash: [: 5.4: integer expression expected
    
  • p. 402: A More Modern Version of test: Before I read this section, I had always wondered why some people used [], others used [[]], and still others used (())! It made Googling for help a challenge because I didn’t understand the different cases and syntaxes. Knowing the similarities and differences will help you as you look for help. Again, don’t focus on the details–you can always look these up. Focus on the concepts.

Ch 28: Reading Keyboard Input

Have a look at this chapter. Taking input from the user while your code is running has many use cases. However, in general, I think most programs should be able to run without interactive input. We want to focus on automation and allowing scripts to run with command line options or configuration files, not on programs that need a user to enter information manually.

It is good to know that these are options, but not something we will spend time on. Implementing user input will likely be an extra credit opportunity on one of the problem sets.

Ch 29: Flow Control: Looping with while / until

The next type of flow control is the while loop. Here is a graphical representation of a while loop:

Diagram of a while loop

The idea here is to do something while some condition is true. Once that condition is false, stop looping and move on with the rest of the program.

  • p. 424: while: Play with the while [[ "$count" -le 5 ]]; script. Read the information about the modification to the read-menu program, but I am not sure it is worth actually writing/running this script.

  • p. 426: Breaking Out of a Loop: the break and continue commands are used a fair bit and have a very different effect:
    • break: Terminates the loop and the program continues with the next command after the loop.
    • continue: stops the current iteration of the loop and resumes the loop with the next iteration.
  • p. 426: while true: This is a handy way of creating what is referred to as an infinite loop. But, by using the break command, you can break out of the loop when some desired condition is met.

  • p.428: until: Here is the diagram for an until loop:

    Diagram of an until loop

    Notice that, opposite of the while loop, in the until loop, the loop executes as long as the condition is false–or until it is true. Choose the loop that make most sense, or is easiest to code for the conditions you want to enforce.

  • p. 428: Reading Files with Loops: Remember that there is a copy of the distros.txt file at /blue/bsc4452/share/Class_Files/TLCL_files/distros.txt. This is a great example of processing a file–both in reading a file line by line, but also in getting fields from that file. Notice how each of the three columns of the dataset are automatically assigned to the three variables–distro, version, release. Also note the next box on p. 429 that would allow you to parse files with different field separators.

Ch 30: Troubleshooting

There is certainly a lot of helpful content in this chapter, but again, I don’t think we have time to cover it in detail. Take a look though. One of the key points about debugging here is that the line numbers called out in the error messages, aren’t always where the error is. If those lines look fine, look above for missing quotes, brackets, etc.

Ch 31: Flow Control: Branching with case

case is another helpful flow control tool, but is not as widely used outside of Bash. For this reason, again, have a look, but don’t worry about the details. Keep it in mind as an option as you write your scripts, and come back to this chapter if needed.

Ch 32: Positional Parameters

This chapter seems a bit out of place and we are going to skip to Ch 33.

Ch 33: Flow Control: Looping with for

Given how much for loops are used, I am surprised William Shotts waits until this late to introduce them! But here we are…Here is a diagram of the for loop:

Diagram of a for loop

The idea is to pass a list of items, or a range of numbers, into the loop and execute the commands in the loop using each item in the list (or range) as the input. Do this for every item in the list and when the program has fished, move on with the rest of the program.

  • p. 466: for: Traditional Shell Form: Here is the example from the text, as well as the same example split across multiple lines:

      [magitz@login2 ~]$ for i in A B C D; do echo $i; done
      A
      B
      C
      D
      [magitz@login2 ~]$ for i in A B C D
      > do
      > echo $i
      > done
      A
      B
      C
      D
      [magitz@login2 ~]$
    

    The for loop is set up with the for command, “i” is a variable to hold each instance of the items in the list, “in” separates the variable and the list of items, and then the items are listed. Within the loop, everything between the “do” and “done” is executed for each item in the list.

  • The list passed into the for loop can take many forms, some examples:

    • for file in *.txt (a list of files ending in .txt in the current directory)
    • for i in {1..30} (a list of numbers from 1-30). This is what the text calls ‘brace expansion’ and can also be used for letter ranges: e.g. {A-L}.
    • for sample in `cat samples.txt` (the lines of the files samples.txt one at a time–the backtick was covered in ch 7. In this case, the list for the for loop is every line in the samples.txt file, one line at a time.)
  • p. 468: You may have noticed back on p. 466 the [in words] part of the for loop syntax is in square brackets, meaning it is optional. This section points out that if you omit the list of items, for iterates over command line options that are passed in. For the example with the longest-word2.sh script (located at /blue/bsc4452/share/Class_Files/TLCL_files/longest-word2.sh), if you run /blue/bsc4452/share/Class_Files/TLCL_files/longest-word2.sh file1 file2 file3, you will get the longest word in each file.

  • p. 469: Why i?: This box, should really be called “Why not i?” As the box describes, i is a commonly used variable, but it is a terrible variable! Especially if your nested loops and have j, and k! Use meaningful variable names! for file in *.txt is much easier to understand. We will look at this more in the Python section, but the developer of Python, Guido van Rossum, is often quoted as saying:

    …code is read much more often than it is written.

    Write your code so that others, and your future self, can read and understand it! While variable names like i and x can be easier to type, the reader will need to pause and figure out what information is i or x holding.

  • p. 468: for: C Language Form: Have a look at this as another way to write for loops. I rarely use this form, but you may run into it. As you might suspect, C programmers are the main users of this in Bash.

  • p. 471: Summing Up: This goes back to looking at each user’s home space. Again, something you probably don’t want to do on the cluster, so skip this, or have a look and see that you understand what is being attempted.

Tags: linux