Bash for loops
11 Apr 2023Open the repo in Rstudio as a project
If you just completed the previous lab module, you will have already cloned the Assignment 2 repository and should have it open in RStudio.
If you have closed RStudio or have a different project open, click on the Project:
menu (upper right corner) and then select Assignment 2
For this lab:
- Graded: answer the questions in the
Assignment_2_FORLOOP_template.md
file located in thescripts
folder. - Not graded, but recommended: keep notes on what you are learning in the
Assignment_2_lab_notebook.md
file.
Background
I have claimed that there are tools at the command line that make automating tasks easier. You are probably wondering when you will get to see that. Now is the time.
Linux For Loops
Open a Linux terminal on your Jetstream Instance
A for loop is a method of iterating over a series of values and performing an operation on each value. All programming languages have some version of this, although the syntax varies.
First a toy example. Let’s say that we have a list of fruit and we want to write a short script that will automatically convert the fruit names to plurals.
First let’s create our items. (Type these commands yourself to get practice)
Pay close attention to the format of the command, there are no spaces on either side of =
this is important and required
fruit_list="banana apple orange grape plum pear durian pineapple"
This creates the variable fruit_list
Remember to refer to a variable after it is defined we place a “$” in front of it (and optionally curly brackets)
echo "${fruit_list}"
Stop and Think (not graded) in your own words, write down what the two previous commands have done. Talk it over with your lab mates.
In unix-bash a for loop has four parts
- A statement saying what items we want to loop over
do
to note the beginning of the commands that we want to loop through- The commands to repeat
done
to indicate the end of the loop
So if we want to loop through each fruit in $fruit_list and print them one at a time we would write
for fruit in ${fruit_list}
do
echo "${fruit}"
done
Translation: for each fruit in the list of fruits $fruit_list
take one value at a time and place it in a new variable $fruit
. Then run the command echo $fruit
to print the current fruit. Go back to the top, place the next fruit in the list into $fruit
and repeat the echo
command. This will continue until there are no more fruits.
What if we want to add an “s” to make these fruit plural?
for fruit in ${fruit_list}
do
echo "${fruit}s"
done
The curly brackets are used to help bash distinguish between the variable name fruit
and the text we want to add s
Exercise One: Write a for loop to pluralize peach, tomato, potato (remember that these end in “es” when plural). Put the code you use (formatted as a code block) into your Assignment_2_FORLOOP_template.md
Hint: I strongly recommend typing your code in your markdown file in an editor (e.g. Rstudio) first. Then cut and paste into the terminal and see if it works. If it does not work, then you can make a change in Rstudio without having to retype the whole thing.
Confused? There are a bunch of tutorials online. I like this one and this one
Interacting with files.
I most commonly use for
loops when I want to process a list of files.
We can use file names as input or output in for
loops. Here is another silly example, but hopefully it illustrates the point. Let’s say the goal was to print the output of every file in a directory.
First set it up:
mkdir for_example
cd for_example
echo "this" > file1.txt
echo "is" > file2.txt
echo "silly" > file3.txt
Stop and Think:(not graded) what did that code block do? If you are unsure, what commands can you use to figure it out? (Hint what directory are you in, what files are in the directory, what are the contents of those files?)
Now to use these files in a for loop:
myfiles=$(ls)
This runs the commands ls
and places the results in the variable myfiles
. The $()
is important and indicates that the ls
command is to be executed. Note that those are smooth brackets, not curly brackets.
echo ${myfiles} # make sure it really does have a list of files
for file in ${myfiles}
do
cat ${file}
done
If you haven’t already done so, run the code above to see what happens.
Exercise Two In your own words provide a human “translation” of the above loop.
Note that we could be more shorthand about this and not bother defining $myfiles
for file in $(ls)
do
cat ${file}
done
Exercise Three Modify the above loop to produce the following output
file file1.txt contains: this
file file2.txt contains: is
file file3.txt contains: silly
hint 1: you will want the command inside the loop to start with echo
. THINK about the difference between echo
and cat
hint 2: you will need to use the $()
inside the for loop, placing a command inside of those parentheses
hint 3: if you aren’t sure where to start, try writing it out in pseudo-code. That is, describe what you want to happen.
If you want a bit more of a challenge, try this (optional)
file “file1.txt” contains: “this”
file “file2.txt” contains: “is”
file “file3.txt” contains: “silly”
Nested for Loops
Sometimes we want to nest one loop inside of another…
hour and day example
What if we wanted to print out the working hours of each day in the work week. We would want to loop through the both the days of the week, and within each day the hours of the day.
for day in mon tue wed thur fri # outer loop: days
do
for hour in 09 10 11 12 01 02 03 04 05 # inner loop: hours
do
echo ${day} ${hour}:00 # print out the day and hour
done
echo # print a blank line between days
done
You will get practice this in the next lab.