Bash Basics: Loops. In this section, you will be introduced to the for, while, and until loops. Boolean and while loops in bash

Loops are an extremely convenient thing when writing any programs or scripts, rather even necessary. They allow us to execute a certain section of code a specified number of times. Naturally, bash has several types of loops. We will describe the cycles for in, for, while, until. Although for in and for are considered different syntaxes of the same statement, in my opinion they differ from each other more than while from until.

Loop with counter for in:

Cycle for in This is a loop with a counter. The block of code located in the body of the loop is repeated as many times as there are values ​​contained in the list of the for in operator, and with each repetition, the counter variable (here it is called var, but of course you can call it whatever you want) has the value of the next element of the list.
If keyword do is on the same line as the word for, then after the list of arguments (before do) you must put a semicolon.
Each of the elements<список>may contain several arguments. This is useful when processing groups of parameters. In this case, to force parsing of each of the arguments in<списке>, you must use the set instruction
You can use a variable as a list in a for loop.
IN<списке>The for loop can use file names, which in turn can contain wildcard characters. This can be very useful when working with a large number of files.
If<список>is not specified in the for loop, then the variable $@ is used as it - a list of command line arguments.
When creating a list of arguments, you can use command substitution in a for loop.
The output of the loop can be redirected from stdout to a file or somewhere else (you can learn more about this by looking at I/O redirection).

Syntax:
for var in<список>
do
<выполняемые команды>
done

Example:
for names in name1 name2 name3 name4
do
echo $names
done

Loop operator for has another way of writing - very similar to the syntax of the for operator in the C language. In this case, when initializing the counters, initial values variables or one variable and after each pass of the loop the condition is checked, if the check returns true, then the next pass of the loop begins. In the block<приращение счётчиков>the value of our variable counters must necessarily change (not necessarily upward) so that when checking the condition, sooner or later we get the value false, otherwise the loop will never end. A very convenient and most importantly familiar option if any operation needs to be repeated a specified number of times.

With a similar syntax:
for ((<инициализация счётчиков>; <проверка условия>; <приращение счётчиков>))
do
<выполняемые команды>
done

Example:
for ((var=1; var<= LIMIT ; var++))
do
echo $var
done

while loop:

This is a fairly simple construction that checks the condition behind the operator while and if this condition is true, it executes the block of commands located between the words do and done and then again proceeds to checking the condition. If the check returns false, the cycle ends and the following commands begin to be executed: done. It is imperative to ensure that<проверка условия>depended on the code running in the loop; otherwise, if the result of the check does not change, you will get an infinite loop.
The standard input device for a while loop can be redirected to a file using the redirection command< в конце цикла.

Syntax:
while<Проверка условия>
do
<Блок команд, обязательно меняющий переменные влияющие на проверку условия>
done

Example:
while [ $var0 -eq 100 ]
do
echo $var
var++
done

Operator while may have several conditions. But only the last of them determines the possibility of continuing the cycle. In this case, the syntax of the loop operator will be different from the usual one.
Syntax(I repeat once again that only the last condition affects the execution of the loop) :
while
<условие1>
<условие2>

<условиеN>
do
<выполняемые команды - тело цикла>
done

Until loop:

Operator until is very similar to while, it also evaluates the condition, but executes the body of the loop if the result of the calculation is false. It may seem unusual, but until evaluates the condition before the first pass of the loop, like while, and not after it. As with for/in loops, when placing the do keyword on the same line as the loop declaration, you must insert a ";" character. before do.
As in the previous case, it is important to remember that the condition must depend on the operations in the loop body, otherwise our script will never complete.

Syntax:
until<Проверка условия>
do
<Блок команд, обязательно меняющий переменные влияющие на проверку условия>
done

Example:
until [ $var0 -gt 100] # The condition is checked at the beginning of the iteration.
do
echo $var
var--
done

That's probably enough for now. :)

  • Back
  • Forward

New articles:

  • Network discovery does not turn on in Windows 7/8/2008/2012
  • Error: This application failed to start because it could not find or load the Qt platform plugin "windows".
  • Configuring automatic restart of rphost.exe worker processes on 1C 8.3 server
  • How to reduce the size of the transaction log (.ldf) in MS SQL 2008/20012

    MS SQL, like any decent industrial DBMS, along with the database, keeps transaction logs that allow you to roll back the state...

BASH Basics. Part 2.
I apologize for such a long delay between articles, but the session makes itself felt at the most inopportune moment :)
Thank you all for the comments, criticism and additions that were voiced in the comments to the last article.
This part, as promised, will be devoted to loops, mathematical operations and the use of external commands.
Let's begin.

Cycles. For-in loop.

The for-in operator is intended for sequential access to the values ​​listed in the list. Each value in turn in the list is assigned to a variable.
The syntax is as follows:
for variable in value_list
do
teams
done

Let's look at a small example:

#!/bin/bash
for i in 0 1 2 3 4 #we will alternately assign values ​​from 0 to 4 inclusive to the variable $i
do
echo "Console number is $i" >> /dev/pts/$i #Write the line "Console number is $i" to the file /dev/pts/$i (virtual terminal file)
done #cycle finished
exit 0

After executing the example, a line with its number will appear in the first 5 virtual consoles (terminals). Values ​​from the list are alternately substituted into the variable $i and the value of this variable is processed in a loop.

Cycles. While loop.

The while loop is more complex than the for-in loop and is used to repeat commands as long as some expression is true (return code = 0).
The operator syntax is as follows:
while expression or command returning a return code
do
teams
done

Let's look at the following example of how the loop works:

#!/bin/bash
again=yes #assign the value "yes" to the variable again
while [ "$again" = "yes" ] #We will execute the loop until $again is equal to "yes"
do
echo "Please enter a name:"
read name
echo "The name you entered is $name"

Echo "Do you wish to continue?"
read again
done
echo "Bye-Bye"


And now the result of the script:
ite@ite-desktop:~$ ./bash2_primer1.sh
Please enter a name:
ite
The name you entered is ite
Do you wish to continue?
yes
Please enter a name:
mihail
The name you entered is mihail
Do you wish to continue?
no
Bye-Bye

As you can see, the loop runs until we enter something other than “yes”. Between do and done you can describe any structures, operators, etc., all of them will be executed in a loop. But you should be careful with this loop, if you run any command in it without changing the expression variable, you can get caught in an endless loop.
Now about the truth condition. After the while, as in the if-then-else conditional statement, you can insert any expression or command that returns the return code, and the loop will be executed until the return code = 0! The "[" operator is analogous to the test command, which checks the truth of the condition that was passed to it.

Let's look at another example, I took it from the book Advanced Bash Scripting. I really liked it :), but I simplified it a little. In this example we will introduce another type of UNTIL-DO loops. This is almost a complete analogue of the WHILE-DO loop, only it is executed while some expression is false.
Here's an example:

#!/bin/bash
echo "Enter numerator: "
read dividend
echo "Enter denominator: "
read divisor

Dnd=$dividend #we will change the variables dividend and divisor,
#let's save their knowledge in other variables, because... they give us
#will be needed
dvs=$divisor
remainder=1

Until [ "$remainder" -eq 0 ]
do
let "remainder = dividend % divisor"
dividend=$divisor
divisor=$remainder
done

Echo "GCD of the numbers $dnd and $dvs = $dividend"


Result of script execution:
ite@ite-desktop:~$ ./bash2_primer3.sh
Enter the numerator:
100
Enter the denominator:
90
GCD of numbers 100 and 90 = 10

Mathematical operations

let command.
The let command performs arithmetic operations on numbers and variables.
Let's look at a small example in which we perform some calculations on the entered numbers:
#!/bin/bash
echo "Enter a: "
read a
echo "Enter b: "
read b

Let "c = a + b" #addition
echo "a+b=$c"
let "c = a / b" #division
echo "a/b=$c"
let "c<<= 2" #сдвигает c на 2 разряда влево
echo "c after shift by 2 bits: $c"
let "c = a % b" # finds the remainder of a divided by b
echo "$a / $b. remainder: $c "


Execution result:
ite@ite-desktop:~$ ./bash2_primer2.sh
Enter a:
123
Enter b:
12
a+b= 135
a/b= 10
c after shift by 2 digits: 40
123 / 12. balance: 3

Well, as you can see, there is nothing complicated, the list of mathematical operations is standard:
+ - addition
- - subtraction
* - multiplication
/ - division
** - exponentiation
% - modulus (modulo division), remainder of division
let allows you to use abbreviations for arithmetic commands, thereby reducing the number of variables used. For example: a = a+b is equivalent to a +=b, etc.

Working with external programs when writing shell scripts

First, some useful theory.
Stream redirection.
Bash (like many other shells) has built-in file descriptors: 0 (stdin), 1 (stdout), 2 (stderr).
stdout - Standard output. Everything that programs output goes here
stdin - Standard input. This is all that the user types in the console
stderr - Standard error output.
For operations with these handles, there are special characters: > (output redirection),< (перенаправление ввода). Оперировать ими не сложно. Например:
redirect the output of the cat /dev/random command to /dev/null (absolutely useless operation :))) or
write the contents of the current directory to the listing file (more useful)
If there is a need to append to a file (when using ">" it will be replaced), you must use ">>" instead of ">"
after asking sudo for the password, it will be taken from the my_password file, as if you had entered it from the keyboard.
If you need to write to a file only errors that could occur while running the program, you can use:
./program_with_error 2> error_file
the number 2 before ">" means that you need to redirect everything that ends up in descriptor 2 (stderr).
If you need to force stderr to write to stdout, then this can be done as follows. way:
the symbol "&" means a pointer to descriptor 1(stdout)
(By default, stderr writes to the console in which the user is working (or rather writes to the display)).
2. Conveyors.
The pipeline is a very powerful tool for working with the Bash console. The syntax is simple:
team1 | command 2 - means that the output of command 1 will be passed as input to command 2
Pipelines can be grouped into chains and output using redirection to a file, for example:
ls -la | grep "hash" |sort > sortilg_list
The output of the ls -la command is passed to the grep command, which selects all lines that contain the word hash and passes it to the command sort, which writes the result to the file sorting_list. Everything is quite clear and simple.

Most often, Bash scripts are used to automate some routine operations in the console, hence sometimes there is a need to process stdout of one command and transfer it to stdin to another command, while the result of one command must be processed in some way. In this section I will try to explain the basic principles of working with external teams inside the script. I think that I have given enough examples and now I can write only the main points.

1. Passing the output to a variable.
In order to record the output of a command into a variable, it is enough to enclose the command in `` quotes, for example
a = `echo "qwerty"`
echo $a

Result: qwerty


However, if you want to store a list of directories in a variable, you must properly process the result to place the data in the variable. Let's look at a small example:
LIST=`find /svn/ -type d 2>/dev/null| awk "(FS="/") (print $4)"| sort|uniq | tr "\n" " "`
for ONE_OF_LIST in $LIST
do
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
done

Here we use a for-do-done loop to archive all directories in the /svn/ folder using the svnadmin hotcopy command (which in our case does not matter, just as an example). The line of greatest interest is: LIST=`find /svn/ -type d 2>/dev/null| awk "(FS="/") (print $4)"| sort|uniq | tr "\n" " "` It assigns the LIST variable to execute find commands, processed by the commands awk, sort, uniq, tr (we will not consider all these commands, because this is a separate article). The LIST variable will contain the names of all directories in the /svn/ folder placed on one line (in order to feed it into the loop.

As you can see, everything is not difficult, just understand the principle and write a couple of your own scripts. In conclusion of the article, I would like to wish you good luck in learning BASH and Linux in general. Criticism, as usual, is welcome. The next article may be devoted to the use of programs such as sed, awk.

One of the basic rules system administration To put it this way: if you need to do the same thing over and over again, write a script and let it do the job for you. If you need to perform some action within a script several times, then you should use cycles. IN GNU Bash you can create loops using constructs for, while And until.

If you've ever been interested in programming, then most likely you're already familiar with these constructs. If, like me, you are studying Bash Without any programming experience, the use of loops may not be obvious enough to understand. Let's start by defining the differences between various types loops, and then move on to examples.

Cycle for is designed to repeat actions until they are all completed. Imagine, for example, that you have a catalog of images and you need to convert them from one format to another. You can use a loop for together with the program convert from the package ImageMagick(or some other program), for example, in order to convert images from JPEG format to PNG format. Or, for example, you may need to convert many audio files from MP3 V OGG Vorbis.

Cycle while used to repeat actions Bye is executed (is true) some condition. Cycle until works a little differently: it performs an action until until the condition is met. So, for example, you can have a counter and perform an action until, until its value reaches 10. Let's look at this in more detail using examples.

Let's start with the cycle for. Its format is:

For i in $(command); do command $i; done

If you are using a loop for in the script, it is better to format it like this:

#!/bin/bash for i in $(command); do command $i done

So, for example, if you need to do backups all HTML files located in the directory, you can use the following command:

For i in $(ls *html); do cp $i $i.bak; done

A local variable is created here $i, the command is executed ls *html, the results of which will be the data that initializes the value of the variable $i at each iteration of the loop (in our example this will be the list of files returned by the command ls, one for each iteration). Next, the command is executed cp, to which a variable is passed among the parameters $i.

Some may ask if it is necessary to use the letter "i" as a variable name? No. You can use any correct Bash variable name. Of course, it's better to use more meaningful variable names in your scripts, like $input or $html.

I gave a very short and simple example of using a loop for. Instead of a command executed in a block do,use echo in order to see the parameters passed to it. This is a very useful practice at the stage of testing scripts, as well as good way help you understand the work in more detail for.

while and until

Let us now consider the constructions while And until. We'll also use bash conditionals a bit. In our example, we will use them to determine, for example, whether the value of a variable is greater or less than the number X; whether the file exists and is a directory. You can also use conditional expressions to determine, for example, whether a file is readable or whether a file has a GID bit in its permissions.

Let's try something simple, like creating some empty files. This is unlikely to be useful to you in life, but as an example, this is it.

#!/bin/bash i=0 while [ $i -lt 22 ] do touch $i i=$[$i+1] done

This script will create 22 files with names from 0 to 21. The loop will run until Bye variable value $i less ( -lt) 22.

Now let's get rid of the created files using a loop until:

#!/bin/bash i=0 until [ $i -eq 22 ] do rm $i i=$[$i+1] done

Here we have replaced while on until, and in the conditional expression they replaced "less" (-lt) on "equals" (-eq). Thus, our script will work as long as the value $i will not reach 22. And instead touch we used rm to delete files rather than create them. Simple, isn't it?

for VAR in 1 2 3...N do done or in one line: for VAR in 1 2 3...N; do ; done
It is possible to substitute both numeric values ​​and ASCII characters into the variable.
Example: $ for i in 1 2 A B Abc ; do echo $i; done 1 2 A B Abc Example of listing files in a variable by “mask” for video transcoding: for i in*.avi; do ; done

2. Substituting the results of another command

for VAR in $(); do ; done
An example of using the results of the seq command: for i in$(seq [KEY]); do echo $i; done$ for i in $(seq 3); do echo $i; done 1 2 3 $ for i in $(seq 3 5); do echo $i; done 3 4 5 $ for i in $(seq 2 2 6); do echo $i; done 2 4 6 An example of using the results of the ls command: $ for i in $(ls /$HOME/Video); do echo $i; done 001.avi 002.avi 003.avi

3. Substitution using C-style (C-style)

for((EXPR1; EXPR2; EXPR3)) do <список команд> done for((i=1; i<=3 ; i++)); do echo $i; done$ for ((i=1; i<=3 ; i++)); do echo $i; done 1 2 3 Подробнее о применении C-style в Bash

4. Enumeration using curly braces (..)

The (START..END) syntax is supported starting with bash version 3.0+, and the (START..END..INCREMENT) syntax is supported starting with bash version 4.0+:

for VAR in {..} do done or for VAR in {....} do done Examples: $ for i in (1..3); do echo $i; done 1 2 3 or $ for i in (4..8..2); do echo $i; done 4 6 8 Counting is possible both for increment and decrement of values: $ for i in (6..-4..3); do echo $i; done 6 3 0 -3

5. Parameter substitution ( in "$@")

Executes commands for each parameter that was passed to the script. for VAR in $@ do done or in one line: for VAR in $@; do ; done
So if you create a script test.sh #!/bin/sh for VAR in $@ do echo$VAR done then when you run it with the parameters: $ ./test.sh param1 param2 param3 param1 param2 param3 Part in$@ can be omitted. Then the test.sh script will be rewritten: #!/bin/sh for VAR do echo$VAR done
Let me give you a couple of examples (with in and without): $ function FUNC_1 ( for VAR in $@; do echo $VAR; done; ) $ FUNC_1 param1 param2 param3 param1 param2 param3 $ function FUNC_2 ( for VAR; do echo $VAR; done; ) $ FUNC_2 param1 param2 param3 param1 param2 param3

6. Using continue and break in a for loop

For all of the above constructs, it is possible to use the commands "continue" to move to the next element of the loop or "break" to exit the loop.

Example (complete when i=6 and don't execute when i=3 and i=5): for i in (1..8); do if[ $i -eq 6 ]; then break; fi if[ $i -eq 3 ] || [ $i -eq 5 ]; then continue; fi echo $i done Execution result: $ $ for i in (1..8); do \ > if [ $i -eq 6 ]; then break; fi; \ > if [ $i -eq 3 ] || [ $i -eq 5 ]; then continue; fi; \ > echo $i; \ > done 1 2 4

A brief description of the difference in loop types:

for - will perform an action as long as there are objects to execute (for example, reading a stream from stdin, a file or a function);
while - performs the action until condition is true;
until - will be executed as long as condition will not become true, i.e. for now it is false .

FOR Loop

Let's consider this version of the script with a loop:

$ cat loop.sh #!/bin/bash for variable in `ls -1` do echo "$variable" done

The syntax is very simple and is quite clearly shown in the example:

for (start the loop) variable (declare a variable on which we will perform actions) in (send a flow to the loop) `ls -1` (command to be executed and passed to the $variable variable). Do and done are the “body” of the loop, within which the main actions will be performed on the received data, and echo “$variable” is the actual action performed by the loop.

Now let’s change the example a little, and instead of explicitly specifying the command, we’ll use the second variable:

$ cat loop.sh #!/bin/bash ls=`ls -1` for variable in $ls do echo "$variable" done

Now the ls -1 command is passed in a separate variable, which allows you to work with the loop more flexibly. Instead of a variable in a loop, you can also use a function:

$ cat loop.sh #!/bin/bash lsl () ( ls -1 ) for variable in `lsl` do echo "$variable" done

The main condition of the for loop is that it will be executed as long as the command passed to it contains objects for action. Based on the example above - as long as ls -1 has files to display - the loop will pass them to a variable and execute the "loop body". As soon as the list of files in the directory ends, the loop will complete its execution.

Let's make the example a little more complicated.

The directory contains a list of files:

$ ls -1 file1 file2 file3 file4 file5 loop.sh nofile1 nofile2 nofile3 nofile4 nofile5

We need to select from them only those that do not have the word " no«:

$ cat loop.sh #!/bin/bash lsl=`ls -1` for variable in $lsl do echo "$variable" | grep -v "no" done $ ./loop.sh file1 file2 file3 file4 file5 loop.sh

You can also use conditional expressions in a loop ( conditional expressions) […] to check conditions and the break statement to interrupt the loop if the condition is triggered.

Consider this example:

$ cat loop.sh #!/bin/bash lsl=`ls -1` for variable in $lsl do if [ $variable != "loop.sh" ] then echo "$variable" | grep -v "no" else break fi done

The loop will run until the file loop.sh is encountered. As soon as the execution of the loop reaches this file, the loop will be interrupted by the break command:

$ ./loop.sh file1 file2 file3 file4 file5

Another example is the use of arithmetic operations immediately before executing the loop body:

$ cat loop.sh #!/bin/bash for ((count=1; count<11; count++)) do echo "$count" done

Here we set three control commands - count=1, a controlling condition - while count is less than 11, and a command to execute - count +1:

WHILE and UNTIL loops

A simple example that clearly demonstrates how the while loop works:

$ cat loop.sh #!/bin/bash count=0 while [ $count -lt 10 ] do ((count++)) echo $count done

We set the $count variable to zero, and then run the whi le loop with the condition “while $count is less than ten, execute the loop.” In the body of the loop we execute postfix increment+1 to the $count variable and the result is printed to stdout.

Execution result:

$ ./loop.sh 1 2 3 4 5 6 7 8 9 10

As soon as the value of the $count variable became 10, the loop stopped.

A good example of an "infinite" loop that demonstrates how while works:

$ cat loop.sh #!/bin/bash count=10 while [ 1 = 1 ] do ((count++)) echo $count done $ ./loop.sh ... 5378 5379 5380 5381 5382 5383 ^C

The until loop works similarly, but in the opposite direction:

$ cat loop.sh #!/bin/bash count=0 until [ $count -gt 10 ] do ((count++)) echo $count done

Here we set a similar condition, but instead of “while the variable is less than 10,” we specify “until the variable becomes greater than 10.” Execution result:

$ ./loop.sh 1 2 3 4 5 6 7 8 9 10 11

If the above example of an “endless loop” is executed using until, it will not output anything, unlike while:

$ cat loop.sh #!/bin/bash count=10 until [ 1 = 1 ] do ((count++)) echo $count done $ ./loop.sh $

Because " condition» originally « true"—the body of the loop will not be executed.

Just like in the for loop, you can use functions in while and until. For example, a loop from a real-life script that checks the server status Tomcat(PID is taken from the system SLES, may differ in other systems), a slightly simplified version:

$ cat loop.sh #!/bin/bash check_tomcat_status () ( RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk "(print $2)"` ) while check_tomcat_status do if [ -n "$ RUN" ] then printf "WARNING: Tomcat still running with PID $RUN."

Execution result:

$ ./loop.sh WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435 26548.WARNING: Tomcat still running with PID 14435

Full version:

Check_tomcat_status () ( RUN=`ps aux | grep tomcat | grep -v grep | grep java | awk "(print $2)"` ) while check_tomcat_status; do if [ -n "$RUN" ] then printf "WARNING: Tomcat is still running with PID $RUN. Stop it? " answer "Stopping Tomcat..." "Proceeding installation..." && $CATALINA_HOME/bin/shutdown. sh 2&>1 /dev/null || break sleep 2 if [ -n "$RUN" ] then printf "Tomcat still running. Kill it? " answer "Killing Tomcat..." "Proceeding installation...n" && kill $RUN || break sleep 2 fi else printf "Tomcat stopped, proceeding...nn" break fi done

The answer function was described in the article, but here a slightly improved version is used:

Answer () ( while read response; do echo case $response in |) printf "$1n" return 0 break ;;

|) printf "$2n" return 1 break ;;