Free Republic
Browse · Search
General/Chat
Topics · Post Article

Skip to comments.

Linux: Bash Loops!
Ryan's Tutorials ^ | Current | Ryan Chadwick

Posted on 03/05/2019 7:25:18 AM PST by ShadowAce

Introduction

Bash loops are very useful. In this section of our Bash Scripting Tutorial we'll look at the different loop formats available to us as well as discuss when and why you may want to use each of them.

Loops allow us to take a series of commands and keep re-running them until a particular situation is reached. They are useful for automating repetitive tasks.

There are 3 basic loop structures in Bash scripting which we'll look at below. There are also a few statements which we can use to control the loops operation.

While Loops

One of the easiest loops to work with is while loops. They say, while an expression is true, keep executing these lines of code. They have the following format:

while [ <some test> ]
do
<commands>
done

You'll notice that similar to if statements the test is placed between square brackets [ ].

In the example below we will print the numbers 1 through to 10:

while_loop.sh

  1. #!/bin/bash
  2. # Basic while loop
  3. counter=1
  4. while [ $counter -le 10 ]
  5. do
  6. echo $counter
  7. ((counter++))
  8. done
  9. echo All done

Let's break it down:

  1. user@bash$./while_loop.sh
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10
  12. All done
  13. user@bash$

A common mistake is what's called an off by one error. In the example above we could have put -lt as opposed to -le (less than as opposed to less than or equal). Had we done this it would have printed up until 9. These mistakes are easy to make but also easy to fix once you've identified it so don't worry too much if you make this error.

Until Loops

The until loop is fairly similar to the while loop. The difference is that it will execute the commands within it until the test becomes true.

until [ <some test> ]
do
<commands>
done

until_loop.sh

  1. #!/bin/bash
  2. # Basic until loop
  3. counter=1
  4. until [ $counter -gt 10 ]
  5. do
  6. echo $counter
  7. ((counter++))
  8. done
  9. echo All done

As you can see in the example above, the syntax is almost exactly the same as the while loop (just replace while with until). We can also create a script that does exactly the same as the while example above just by changing the test accordingly.

So you may be asking, 'Why bother having the two different kinds of loops?'. We don't necessarily. The while loop would be able to handle every scenario. Sometimes, however, it just makes it a little easier to read if we phrase it with until rather than while. Think about the following statement:

Leave the towel on the line until it's dry.

We could have said:

Leave the towel on the line while it is not dry.

Or:

Leave the towel on the line while it is wet.

But they just don't seem as elegant and easy to understand. So by having both while and until we can pick whichever one makes the most sense to us and as a result, end up with code that is easier for us to understand when we read it.

We should always strive for clean, obvious and elegant code when writing our Bash scripts.

For Loops

The for loop is a little bit different to the previous two loops. What it does is say for each of the items in a given list, perform the given set of commands. It has the following syntax.

for var in <list>
do
<commands>
done

The for loop will take each item in the list (in order, one after the other), assign that item as the value of the variable var, execute the commands between do and done then go back to the top, grab the next item in the list and repeat over.

The list is defined as a series of strings, separated by spaces.

Here is a simple example to illustrate:

for_loop.sh

  1. #!/bin/bash
  2. # Basic for loop
  3. names='Stan Kyle Cartman'
  4. for name in $names
  5. do
  6. echo $name
  7. done
  8. echo All done

Let's break it down:

  1. ./for_loop.sh
  2. Stan
  3. Kyle
  4. Cartman
  5. All done

Ranges

We can also process a series of numbers

for_loop_series.sh

  1. #!/bin/bash
  2. # Basic range in for loop
  3. for value in {1..5}
  4. do
  5. echo $value
  6. done
  7. echo All done

  1. ./for_loop_series.sh
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. All done

When specifying a range you may specify any number you like for both the starting value and ending value. The first value may also be larger than the second in which case it will count down.

It is also possible to specify a value to increase or decrease by each time. You do this by adding another two dots ( .. ) and the value to step by.

for_loop_stepping.sh

  1. #!/bin/bash
  2. # Basic range with steps for loop
  3. for value in {10..0..2}
  4. do
  5. echo $value
  6. done
  7. echo All done

  1. ./for_loop.sh
  2. 10
  3. 8
  4. 6
  5. 4
  6. 2
  7. 0
  8. All done

One of the more useful applications of for loops is in the processing of a set of files. To do this we may use wildcards. Let's say we want to convert a series of .html files over to .php files.

convert_html_to_php.sh

  1. #!/bin/bash
  2. # Make a php copy of any html files
  3. for value in $1/*.html
  4. do
  5. cp $value $1/$( basename -s .html $value ).php
  6. done

Controlling Loops: Break and Continue

Most of the time your loops are going to through in a smooth and ordely manner. Sometimes however we may need to intervene and alter their running slightly. There are two statements we may issue to do this.

Break

The break statement tells Bash to leave the loop straight away. It may be that there is a normal situation that should cause the loop to end but there are also exceptional situations in which it should end as well. For instance, maybe we are copying files but if the free disk space get's below a certain level we should stop copying.

copy_files.sh

  1. #!/bin/bash
  2. # Make a backup set of files
  3. for value in $1/*
  4. do
  5. used=$( df $1 | tail -1 | awk '{ print $5 }' | sed 's/%//' )
  6. if [ $used -gt 90 ]
  7. then
  8. echo Low disk space 1>&2
  9. break
  10. fi
  11. cp $value $1/backup/
  12. done

Continue

The continue statement tells Bash to stop running through this iteration of the loop and begin the next iteration. Sometimes there are circumstances that stop us from going any further. For instance, maybe we are using the loop to process a series of files but if we happen upon a file which we don't have the read permission for we should not try to process it.

copy_check.sh

  1. #!/bin/bash
  2. # Make a backup set of files
  3. for value in $1/*
  4. do
  5. if [ ! -r $value ]
  6. then
  7. echo $value not readable 1>&2
  8. continue
  9. fi
  10. cp $value $1/backup/
  11. done

Select

The select mechanism allows you to create a simple menu system. It has the following format:

select var in <list>
do
<commands>
done

When invoked it will take all the items in list (similar to other loops this is a space separated set of items) and present them on the screen with a number before each item. A prompt will be printed after this allowing the user to select a number. When they select a number and hit enter the corresponding item will be assigned to the variable var and the commands between do and done are run. Once finished a prompt will be displayed again so the user may select another option.

A few points to note:

Here is a simple example to illustrate it's usage:

select_example.sh

  1. #!/bin/bash
  2. # A simple menu system
  3. names='Kyle Cartman Stan Quit'
  4. PS3='Select character: '
  5. select name in $names
  6. do
  7. if [ $name == 'Quit' ]
  8. then
  9. break
  10. fi
  11. echo Hello $name
  12. done
  13. echo Bye

Let's break it down:

And now let's run the Bash script:

  1. ./select_example.sh
  2. 1) Kyle     3) Stan
  3. 2) Cartman  4) Quit
  4. Select character: 2
  5. Hello Cartman
  6. Select Character: 1
  7. Hello Kyle
  8. Select character: 4
  9. Bye

Summary

while do done
Perform a set of commands while a test is true.

until do done
Perform a set of commands until a test is true.

for do done
Perform a set of commands for each item in a list.

break
Exit the currently running loop.

continue
Stop this iteration of the loop and begin the next iteration.

select do done
Display a simple menu system for selecting items from a list.

Clarity
There are several Bash loop mechanisms. Pick the one which makes your code the easiest to follow.

Planning
Now that your scripts are getting a little more complex you will probably want to spend a little bit of time thinking about how you structure them before diving in.

Activities

Now we have quite a collection of tools under our belt, we can tackle some more interesting problems.



TOPICS: Computers/Internet
KEYWORDS: linux

1 posted on 03/05/2019 7:25:18 AM PST by ShadowAce
[ Post Reply | Private Reply | View Replies]

To: rdb3; Calvinist_Dark_Lord; JosephW; Only1choice____Freedom; Ernest_at_the_Beach; martin_fierro; ...

2 posted on 03/05/2019 7:27:02 AM PST by ShadowAce (Linux - The Ultimate Windows Service Pack)
[ Post Reply | Private Reply | To 1 | View Replies]

To: ShadowAce

Loops are bad
Loops are a pain
Loops make you do things
again and again

... sorry wrong type of bash


3 posted on 03/05/2019 7:41:08 AM PST by taxcontrol
[ Post Reply | Private Reply | To 1 | View Replies]

To: ShadowAce

An imminently practical function. One habitual task I use it for is to mute system sound on my PC for a prescribed period of time. Very handy when listening to streaming talk radio because they all seem to only be able to sell ads for men’s virility pills and ambulance-chasing lawyers, which I choose not to listen to.

At invocation, the script mutes the master system sound volume, then asks me how long I want to mute it for. Then it uses a while/if loop of the “sleep” function to count down that many seconds before un-muting.

I wrote similar batch file for Windows using if/then looping. It’s such a relief not to have to listen to that moronic woman teach me how to spell the name of her dog food.


4 posted on 03/05/2019 8:31:03 AM PST by Paal Gulli
[ Post Reply | Private Reply | To 1 | View Replies]

To: ShadowAce
Loops are awesome and powerful. One really useful thing that you can do in a loop is to test for the existence (or absense) of a file. If the file exists, you can do one thing, if the file doesn't you can do another.

In the following the script checks for the file /tmp/i_exist. If it is there, it sleeps a bit and checks again. Once the file is deleted, it exits. You can use this as a poor man's job control. Let's say you have a job that kicks off every day and does some work. Let's also say that you have a separate job that needs to run, but it is dependent upon the first job finishing successfully before it can run. You can have the first job create a check file, and delete it when it finishes, which will allow the 2nd to execute.

$ cat loopers
#!/bin/bash

while [ -f /tmp/i_exist ]
do
echo "It exists"
sleep 5
done
echo "It no longer exists. Exiting"
exit 0

$ ./loopers 
It exists
It exists
It exists
It exists
It no longer exists. Exiting
$

Another use for something like this is to test to see if a job is already running. You could have the script create a run file. If the job is running it exists. If you start the same job a second time, it could see that the file already exists, and refuse to run. There are other possible ways to do this as well, but this is quick and dirty, and generally works, as long as the job completes successfully and cleans up after itself. If the script is killed while executing, it might not do so, so subsequent executions will see the file and thus refuse to run.

$ cat loopers
#!/bin/bash

if [ -f /tmp/i_exist ]
do
echo "Check file exists! Check to see if another copy of this program is running."
exit 1
done
echo "Now I can create the file"
touch /tmp/i_exist
echo "Real work would be done here, since the file didn't exist."
echo "Now I clean up after myself..."
rm /tmp/i_exist
exit 0

Note that above I used "if" instead of 'while'. Either would work in this instance.

5 posted on 03/05/2019 8:33:58 AM PST by zeugma (Power without accountability is fertilizer for tyranny.)
[ Post Reply | Private Reply | To 1 | View Replies]

To: zeugma

Syntax error. In the second script the “do” should be “then”. That’s what I get for writing on the fly without testing.


6 posted on 03/05/2019 8:36:52 AM PST by zeugma (Power without accountability is fertilizer for tyranny.)
[ Post Reply | Private Reply | To 5 | View Replies]

Disclaimer: Opinions posted on Free Republic are those of the individual posters and do not necessarily represent the opinion of Free Republic or its management. All materials posted herein are protected by copyright law and the exemption for fair use of copyrighted works.

Free Republic
Browse · Search
General/Chat
Topics · Post Article

FreeRepublic, LLC, PO BOX 9771, FRESNO, CA 93794
FreeRepublic.com is powered by software copyright 2000-2008 John Robinson