Bash 08 – Arrays and For Loops

Jarret B

Well-Known Member
Staff member
Joined
May 22, 2017
Messages
339
Reaction score
369
Credits
11,689
This article is another one that contains some very important information. Arrays are useful in any programming language and Bash is no exception.

Arrays are like variable because it holds a value. Let’s look at arrays a little more.

Array

An array can hold multiple values, and an index manages each element of information.

Let’s look at an example to make this a little easier to understand. Let’s assume we have an array containing 12 entries. Each entry is an element, and each element has a numeric value to access the individual element. The numeric value is called an index. Look at:

0 January
1 February
2 March
3 April
4 May
5 June
6 July
7 August
8 September
9 October
10 November
11 December


Now, you may wonder why the first index is not 1. In Bash, and some other languages, indexes start at 0 and not 1.

If we were going to access a specific element, we can take the number of the month and subtract one to get the proper index value. For example, October is the tenth month. The Element at index 10 is ‘November’. This is a problem, but easily fixed by subtracting one from the month's number before using it as an index number. If we subtract one, we get 9, which is ‘October’.

You use a variable name for the array and an index value to access an array.

Let’s look at creating an array as we used as an example:

Code:
months=(January February March April May June July August September October November December)

It may be easy to open a terminal and paste the above line into it. Press enter to execute the line. The array is now created in the current terminal environment. It will remain until you close the terminal.

To access an element, you use the variable name surrounded by curly brackets ({}). After the variable, you include the index number in square braces ([]). So, if we wanted to use index 5, the reference would be ‘${months[5]}’.

Try the command ‘echo ${months[5]}’. The result should be ‘June’.

If you should enter no index value, such as ‘echo {months}’, the default index is ‘0’. The command would return ‘January’.

To see a full list of all elements, use an index value of ‘@’. Such as ‘echo ${months[@]}’.

If you should want only the last half of the elements, you can specify a starting point to begin the output: ‘‘echo ${months[@]:6}’. If you want to start in the middle (6) and print out 3 months, the command is ‘echo ${months[@]:6:3}’. The output should be ‘July August September’.

Let’s look at deleting an element from the array. We use the command ‘unset’ and then specify the array name and the index number we want to remove. Here, let’s remove the last element (11). The command is ‘unset months[11]’.

If you echo out the whole list of elements, see that ‘December’ is now gone.

To add an element, we add it to the end of the list. To add back December, we use the command ‘months+=(December)’. If you print out all the elements, you’ll see that December is back again.

Let’s say you had initially entered the command ‘months=(January February March April May July August September October November December)’ and you were working with the array and realized you forgot ‘June’. You could retype the line correctly, or insert the missing month.

To do this, we need to take the existing array in parts. We need the first part, January to May, and the second part, July to December.

NOTE: you can do this easily with the command ‘months=(January February March April May July August September October November December)’, to overwrite the array. You can also ‘unset months[5]’.

The command needed is ‘months=( "${months[@]:0:5}" "June" "${months[@]:5}" )’. We can use the current array to create a new array. We’ll need to keep the elements from index 0-5, which is the first portion of the command ("${months[@]:0:5}"). At index 0, we take 5 elements. Adding "June" is the next portion of the command. The last portion is to use the remaining elements starting at index 5 to the end (July to December). The new array is now recreated.

If you were to unset an element, you need to realize that the specified index no longer exists. To see a list of the index numbers, use the command ‘echo {!months[@]}’. If you create the ‘months’ array, and then unset index 5, use the echo command with the exclamation mark. Try:

Code:
months=(January February March April May June July August September October November December)
unset months[5]
echo ${!months[@]}

You should see the list of index numbers: ‘0 1 2 3 4 6 7 8 9 10 11’. You’ll see that index 5 is now missing. Using the unset command, we remove the element and the index number. If you use the command ‘echo ${months[5]}’, you get an error. We can easily add the value back with ‘months[5]="June".

If you have an array that you remove an element from and then need to re-index the elements, we could use the command ‘months=( "${months[@]}" )’. This will re-index the existing elements, so there are no missing index numbers. Of course, for our example, a missing month would make no sense, but know it is possible.

If you want to know the number of elements in an array, use the command ‘echo ${#months[@]}’. If all the months are in the array, then the result will be 12.

Reading an Array from a File

Sometimes, the list of elements can be lengthy and you do not want to type in each element in a line. If the elements already exist in a file, then you can read in the data into the array elements.

For this example, I will type a text file named ‘Months.txt’ and list each month on a separate line. From the script, I will use the line:

Code:
readarray months < /home/jarret/Scripts/Months.txt

The command ‘readarray’ creates the array, in this case named ‘months’. It then reads in the file after the less-than redirect. I used the full path to the text file just in case the script is located somewhere other than the same folder as the text file.

After the ‘readarray’ line, the command ‘echo ${months[@]}’ shows that we have read the text file into the array.

Let’s try another command, ‘echo "The second month, ${months[1]}, is the best month!"’. The output is not what you expect. The output is on two lines. Since we input the elements from a file, each line ends with an invisible newline character. The newline character is present in each element, so it’s included in the array elements.

When using the ‘readarray’ command, the parameter ‘-t’ is used to read each line and omit the newline character when placed into the array.

To see the invisible newline characters, you can output the array with the command:

Code:
echo ${months[@]@Q}

The extra ‘@Q’ will show the invisible newline characters in the output.

Instead of using a file, what if we wanted the output of a command to be placed into an array? Let’s say we anted to place all files from our Home folder into an array.

We need to pipe the list from the command ‘ls ~’. This information needs to be piped into the array created by ‘readarray’. We can then output the input to the screen:

Code:
readarray -t files < <(ls ~)
echo ${files[@]}

The output should comprise all the files and folders in the Home folder of the current user. I also used the ‘-t’ parameter to drop the newline characters.

For Loops

A For Loop is a structure in Bash that works well with arrays or lists.

Let’s look at the structure of a For Loop.

Code:
for element in list/array; do
   command_1
   command_n
done

Let’s look at a small list first. The list will comprise four genres: sci-fi, fantasy, comedy and anime. With each genre, we will create a folder in a folder named ‘Movies’ that is in the Home folder under ‘Videos’. The script would look like:

GNU nano 6.2 forloop.sh

Code:
#!/bin/bash
mkdir ~/Videos/Movies
for folder in sci-fi fantasy comedy anime; do
   mkdir ~/Videos/Movies/$folder
done
cd ~/Videos/Movies
ls

Now, let’s look at this in depth. After the shebang line, we create the folder ‘Movies’ in the ‘Videos’ folder that already exists.

In the next line, we have the main statement of our For Loop. We are using a variable named ‘folder’ that will contain an element from our list. After the word ‘in’, we make our list and end it with the semi-colon. The line finishes with the word ‘do’.

With the For statement, the system will process one element from the list at a time and place it in the variable ‘folder’. Since there are four elements, the system will process the For Loop four times. There is only one command in the For Loop to be processed. The command it processes is to make a directory named in the variable ‘folder’ in the specified folder of ‘~/Videos/Movies/’. The script passes through the For Loop four times and then exits. After the For Loop at the ‘done’ statement, the system changes into the ‘Movies’ folder, then finally lists the contents that were created.

If we want to use an array in a For Loop, we just replace the list with the array. Say, for instance, we had an array named ‘genre’ that contained a list of movie genres. Instead of using the list, as above, we can use the array. The only line to change is the ‘for’ statement, which would be:

Code:
for folder in “${genre[@]}”; do

Using a For Loop to process each entry in an array can simplify things rather than using a list.

Conclusion

These new techniques should help you do more with Bash.

Try a few of your own examples and see what you can do with arrays and For Loops.
 


Top