For var in list do command done
In each iteration of the loop, the next value from the list will be written to the var variable. The first pass of the loop will therefore use the first value from the list. In the second - the second, and so on - until the loop reaches the last element.
#!/bin/bash for var in first second third fourth fifth do echo The $var item done
The results of this script are shown below. You can clearly see that the $var variable contains elements from the list sequentially. This happens until the cycle reaches the last of them.
Simple for loop
Please note that the $var variable retains its value when exiting the loop, its contents can be changed, and in general, you can work with it like any other variable.
#!/bin/bash for var in first "the second" " the third" "I'll do it" do echo "This is: $var" done
This is what happens after this loop goes through the list. As you can see, the result is quite expected.
Iterating over complex values
TNW-CUS-FMP - promo code for a 10% discount on our services, available for activation within 7 days"
#!/bin/bash file="myfile" for var in $(cat $file) do echo " $var" done
This example uses the cat command, which reads the contents of a file. The resulting list of values is passed into the loop and displayed on the screen. Please note that the file we are accessing contains a list of words separated by newlines; no spaces are used.
A loop that loops through the contents of a file
Here we must take into account that such an approach, if line-by-line data processing is expected, will not work for a file of a more complex structure, the lines of which may contain several words separated by spaces. The loop will process individual words, not lines.
What if this is not what you need at all?
To resolve the issue, you can temporarily change the IFS environment variable. Here's how to do it in a bash script, assuming you only need a newline as a field separator:
IFS=$"\n"
Once you add this command to your bash script, it will work as expected, ignoring spaces and tabs and treating only newline characters as field separators.
#!/bin/bash file="/etc/passwd" IFS=$"\n" for var in $(cat $file) do echo " $var" done
If this script is run, it will output exactly what is required of it, giving, in each iteration of the loop, access to the next line written to the file.
Line by line traversal of a file in a for loop
Separators can also be other characters. For example, above we displayed the contents of the /etc/passwd file. User data on lines is separated by colons. If you need to process such strings in a loop, IFS can be configured like this:
For example, here's how to list files and folders:
#!/bin/bash for file in /home/likegeeks/* do if [ -d "$file" ] then echo "$file is a directory" elif [ -f "$file" ] then echo "$file is a file" fi done
If you understood the previous material in this series of articles, you should understand the structure of the if-then construct, as well as how to distinguish a file from a folder. If you find it difficult to understand the above code, re-read this material.
This is what the script will output.
Displaying the contents of a folder
Notice how we initialize the loop, namely the wildcard "*" at the end of the folder address. This symbol can be thought of as a wildcard meaning: “all files with any names.” it allows you to organize automatic substitution of file names that match the pattern.
When testing a condition in an if statement, we enclose the variable name in quotes. This is done because the file or folder name may contain spaces.
For (i = 0; i< 10; i++)
{
printf("number is %d\n", i);
}
In bash scripts you can use for loops, the description of which looks very similar to C-style loops, although there are some differences. The cycle diagram with this approach looks like this:
For ((initial value of variable; condition for ending loop; change of variable))
In bash it can be written like this:
For ((a = 1; a< 10; a++))
Here's a working example:
#!/bin/bash for ((i=1; i<= 10; i++))
do
echo "number is $i"
done
This code will output a list of numbers from 1 to 10.
Looping in C style
Here is a diagram of the organization of while loops
while condition check command
do
other teams
done
Let's take a look at an example script with a loop like this:
#!/bin/bash var1=5 while [ $var1 -gt 0 ] do echo $var1 var1=$[ $var1 - 1 ] done
At the entrance to the loop, it is checked whether the variable $var1 is greater than zero. If so, the body of the loop is executed, in which one is subtracted from the value of the variable. This happens in each iteration, and we print the value of the variable to the console before it is modified. As soon as $var1 reaches the value 0, the loop stops.
Result of the while loop
If you do not modify the $var1 variable, this will cause the script to end up in an infinite loop.
#!/bin/bash for ((a = 1; a<= 3; a++))
do
echo "Start $a:"
for ((b = 1; b <= 3; b++))
do
echo " Inner loop: $b"
done
done
Below is what this script will output. As you can see, first the first iteration of the outer loop is executed, then three iterations of the inner one, after its completion the outer loop comes into play again, then the inner one again.
Nested Loops
#!/bin/bash IFS=$"\n" for entry in $(cat /etc/passwd) do echo "Values in $entry –" IFS=: for value in $entry do echo " $value" done done
There are two loops in this script. The first one traverses the lines using the newline character as a delimiter. The internal one is busy parsing strings whose fields are separated by colons.
File Data Processing
This approach can be used when processing CSV files, or any similar files, by writing the delimiter character into the IFS environment variable as needed.
#!/bin/bash for var1 in 1 2 3 4 5 6 7 8 9 10 do if [ $var1 -eq 5 ] then break fi echo "Number: $var1" done
Such a loop, under normal conditions, will go through the entire list of values from the list. However, in our case, its execution will be interrupted when the $var1 variable is equal to 5.
Exiting a for loop early
Here is the same thing, but for the while loop:
#!/bin/bash var1=1 while [ $var1 -lt 10 ] do if [ $var1 -eq 5 ] then break fi echo "Iteration: $var1" var1=$(($var1 + 1)) done
The break command, executed when $var1 reaches 5, breaks the loop. The console will display the same thing as in the previous example.
#!/bin/bash for ((var1 = 1; var1< 15; var1++))
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
then
continue
fi
echo "Iteration number: $var1"
done
When the condition inside the loop is satisfied, that is, when $var1 is greater than 5 and less than 10, the shell executes the continue command. This results in skipping the remaining commands in the body of the loop and moving on to the next iteration.
The continue command in a for loop
For example, instead of showing on the screen what is output in a loop, you can write it all to a file or pass it somewhere else:
#!/bin/bash for ((a = 1; a< 10; a++))
do
echo "Number is $a"
done >myfile.txt echo "finished."
The shell will create the file myfile.txt and redirect the output of the for statement to that file. Let's open the file and make sure that it contains exactly what we expect.
Redirect loop output to file
#!/bin/bash IFS=: for folder in $PATH do echo "$folder:" for file in $folder/* do if [ -x $file ] then echo " $file" fi done done
This script, small and simple, allowed us to get a list of executable files stored in folders from PATH.
Searching for executable files in folders from the PATH variable
If we assume that you are a developer of bash scripts who knows about them only what is stated in the first part of this series of articles, and in this second, then you can already write something useful. Ahead is the third part, after understanding which you will learn how to pass parameters and command line switches to bash scripts, and what to do with it all.
The bash shell supports for loops, which allow you to iterate over sequences of values. Here is the basic structure of such loops:For var in list do command done
In each iteration of the loop, the next value from the list will be written to the var variable. The first pass of the loop will therefore use the first value from the list. In the second - the second, and so on - until the loop reaches the last element.
#!/bin/bash for var in first second third fourth fifth do echo The $var item done
The results of this script are shown below. You can clearly see that the $var variable contains elements from the list sequentially. This happens until the cycle reaches the last of them.
Simple for loop
Please note that the $var variable retains its value when exiting the loop, its contents can be changed, and in general, you can work with it like any other variable.
#!/bin/bash for var in first "the second" "the third" "I’ll do it" do echo "This is: $var" done
This is what happens after this loop goes through the list. As you can see, the result is quite expected.
Iterating over complex values
TNW-CUS-FMP - promo code for a 10% discount on our services, available for activation within 7 days"
#!/bin/bash file="myfile" for var in $(cat $file) do echo " $var" done
This example uses the cat command, which reads the contents of a file. The resulting list of values is passed into the loop and displayed on the screen. Please note that the file we are accessing contains a list of words separated by newlines; no spaces are used.
A loop that loops through the contents of a file
Here we must take into account that such an approach, if line-by-line data processing is expected, will not work for a file of a more complex structure, the lines of which may contain several words separated by spaces. The loop will process individual words, not lines.
What if this is not what you need at all?
To resolve the issue, you can temporarily change the IFS environment variable. Here's how to do it in a bash script, assuming you only need a newline as a field separator:
IFS=$"\n"
Once you add this command to your bash script, it will work as expected, ignoring spaces and tabs and treating only newline characters as field separators.
#!/bin/bash file="/etc/passwd" IFS=$"\n" for var in $(cat $file) do echo " $var" done
If this script is run, it will output exactly what is required of it, giving, in each iteration of the loop, access to the next line written to the file.
Line by line traversal of a file in a for loop
Separators can also be other characters. For example, above we displayed the contents of the /etc/passwd file. User data on lines is separated by colons. If you need to process such strings in a loop, IFS can be configured like this:
For example, here's how to list files and folders:
#!/bin/bash for file in /home/likegeeks/* do if [ -d "$file" ] then echo "$file is a directory" elif [ -f "$file" ] then echo "$file is a file" fi done
If you've been through this series of articles, you should understand the structure of the if-then construct, as well as how to distinguish a file from a folder. If you find it difficult to understand the above code, re-read this material.
This is what the script will output.
Displaying the contents of a folder
Notice how we initialize the loop, namely the wildcard "*" at the end of the folder address. This symbol can be thought of as a wildcard meaning: “all files with any names.” it allows you to organize automatic substitution of file names that match the pattern.
When testing a condition in an if statement, we enclose the variable name in quotes. This is done because the file or folder name may contain spaces.
For (i = 0; i< 10; i++)
{
printf("number is %d\n", i);
}
In bash scripts you can use for loops, the description of which looks very similar to C-style loops, although there are some differences. The cycle diagram with this approach looks like this:
For ((initial value of variable; condition for ending loop; change of variable))
In bash it can be written like this:
For ((a = 1; a< 10; a++))
Here's a working example:
#!/bin/bash for ((i=1; i<= 10; i++))
do
echo "number is $i"
done
This code will output a list of numbers from 1 to 10.
Looping in C style
Here is a diagram of the organization of while loops
while condition check command
do
other teams
done
Let's take a look at an example script with a loop like this:
#!/bin/bash var1=5 while [ $var1 -gt 0 ] do echo $var1 var1=$[ $var1 - 1 ] done
At the entrance to the loop, it is checked whether the variable $var1 is greater than zero. If so, the body of the loop is executed, in which one is subtracted from the value of the variable. This happens in each iteration, and we print the value of the variable to the console before it is modified. As soon as $var1 reaches the value 0, the loop stops.
Result of the while loop
If you do not modify the $var1 variable, this will cause the script to end up in an infinite loop.
#!/bin/bash for ((a = 1; a<= 3; a++))
do
echo "Start $a:"
for ((b = 1; b <= 3; b++))
do
echo " Inner loop: $b"
done
done
Below is what this script will output. As you can see, first the first iteration of the outer loop is executed, then three iterations of the inner one, after its completion the outer loop comes into play again, then the inner one again.
Nested Loops
#!/bin/bash IFS=$"\n" for entry in $(cat /etc/passwd) do echo "Values in $entry –" IFS=: for value in $entry do echo " $value" done done
There are two loops in this script. The first one traverses the lines using the newline character as a delimiter. The internal one is busy parsing strings whose fields are separated by colons.
File Data Processing
This approach can be used when processing CSV files, or any similar files, by writing the delimiter character into the IFS environment variable as needed.
#!/bin/bash for var1 in 1 2 3 4 5 6 7 8 9 10 do if [ $var1 -eq 5 ] then break fi echo "Number: $var1" done
Such a loop, under normal conditions, will go through the entire list of values from the list. However, in our case, its execution will be interrupted when the $var1 variable is equal to 5.
Exiting a for loop early
Here is the same thing, but for the while loop:
#!/bin/bash var1=1 while [ $var1 -lt 10 ] do if [ $var1 -eq 5 ] then break fi echo "Iteration: $var1" var1=$(($var1 + 1)) done
The break command, executed when $var1 reaches 5, breaks the loop. The console will display the same thing as in the previous example.
#!/bin/bash for ((var1 = 1; var1< 15; var1++))
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
then
continue
fi
echo "Iteration number: $var1"
done
When the condition inside the loop is satisfied, that is, when $var1 is greater than 5 and less than 10, the shell executes the continue command. This results in skipping the remaining commands in the body of the loop and moving on to the next iteration.
The continue command in a for loop
For example, instead of showing on the screen what is output in a loop, you can write it all to a file or pass it somewhere else:
#!/bin/bash for ((a = 1; a< 10; a++))
do
echo "Number is $a"
done >myfile.txt echo "finished."
The shell will create the file myfile.txt and redirect the output of the for statement to that file. Let's open the file and make sure that it contains exactly what we expect.
Redirect loop output to file
#!/bin/bash IFS=: for folder in $PATH do echo "$folder:" for file in $folder/* do if [ -x $file ] then echo " $file" fi done done
This script, small and simple, allowed us to get a list of executable files stored in folders from PATH.
Searching for executable files in folders from the PATH variable
If we assume that you are a developer of bash scripts who knows about them only what is stated in this series of articles, and in this second one, then you can already write something useful. Ahead is the third part, after understanding which you will learn how to pass parameters and command line switches to bash scripts, and what to do with it all.
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 .
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 continue 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 body of the loop:
$ 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:
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." else printf "Tomcat stopped, proceeding...nn" break fi done
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 ;; *) printf "Please, enter Y(yes) or N(no)! " esac done )
Here it was possible to use both while and until - but not a for loop, since for would have worked once (received the PID and ended).
for VAR in 1 2 3...N doThe (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 {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