How to write loop in Bash?

How to write loop in Bash?

Use the for loop and the find command to automatically perform a set of operations on multiple files.

A common reason people want to learn the Unix shell is to release the batch function. If you want to perform certain operations on many files, one method is to construct a command to traverse these files. In programming terminology, this is called execution control, and one of the most common examples is the for loop.

The for loop is a recipe that details what you want the computer to do with each specified data object (such as a file).

Classic loop

Linux terminal 7 major terminal emulators for Linux 10 command-line tools for data analysis in Linux Download now: SSH cheat sheet Advanced Linux command cheat sheet Linux command line tutorial A simple loop is the loop for analyzing file collection . This may not be a useful loop in itself, but it is a safe method that can prove to you that you have the ability to process each file in the directory separately. First, create a simple test environment by creating directories and placing certain copies of some files in it. You can use any file at the beginning, but later examples require graphic files (such as JPEG, PNG, or similar files). You can use the file manager or create a folder in the terminal and copy the files into it:

  •  
  •  
$ mkdir example        $ cp ~/Pictures/vacation/*.{png,jpg} example

Change the directory to a new folder, and then list the files in it to confirm that the test environment meets your expectations:

  •  
  •  
  •  
  •  
  •  
  •  
$ cd example$ ls -1cat.jpgdesign_maori.pngotago.jpgwaterfall.png

The syntax for traversing each file one by one in a loop is: create a variable. Then define the data set that you want the variable to loop through. In this case, use wildcards to cycle through all files in the current directory (wildcards match everything). Then terminate the introductory clause with a semicolon (;).

  •  
$ for f in * ;

According to your preferences, you can choose to return here. The shell will not attempt to execute the loop until it is syntactically completed.

Next, define what you want to happen in each loop iteration. For simplicity, use the file command to obtain a small amount of data about each file, which is represented by the f variable (but starts with $, telling the shell to replace the value of the variable with the currently contained variable):

  •  
do file $f ;

Terminate the clause with another semicolon and close the loop:

  •  
done

When you are done, press Return to start the Shell to cycle through all the contents in the current directory. The for loop assigns each file to the variable f one by one, and then runs the command:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
$ for f in * ; do        > file $f ;        > done        cat.jpg: JPEG image data, EXIF standard 2.2        design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced        otago.jpg: JPEG image data, EXIF standard 2.2        waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

You can also write like this:

  •  
  •  
  •  
  •  
  •  
$ for f in *; do file $f; done        cat.jpg: JPEG image data, EXIF standard 2.2        design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced        otago.jpg: JPEG image data, EXIF standard 2.2        waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

The multi-line and single-line formats are the same for your shell and produce exactly the same results.

A practical example

 

This is a practical example of how loops are useful for daily calculations. Let ’s say you have a collection of vacation photos to send to a friend. Your photo file is very large, too large to be emailed, and it is inconvenient to upload to your photo sharing service. You want to create a smaller web version of a photo, but you have 100 photos and do n’t want to waste time shrinking each photo one by one.

First, use the package manager to install the ImageMagick command on Linux, BSD, or Mac. For example, on Fedora and RHEL:

  •  
$ sudo dnf install ImageMagick

On Ubuntu or Debian:

  •  
$ sudo apt install ImageMagick

On BSD, use the port or pkgsrc. On Mac, use Homebrew or MacPorts.

After installing ImageMagick, you will have a new set of commands for manipulating photos.

Create a target directory for the file you want to create:

  •  
$ mkdir tmp

To shrink each photo to 33% of its original size, try the following loop:

  •  
$ for f in * ; do convert $f -scale 33% tmp/$f ; done

Then view the zoomed photos in the tmp folder.

You can use any number of commands in the loop, so if you need to perform complex operations on a batch of files, you can put the entire workflow between the do and done statements of the for loop. For example, suppose you want to copy each processed photo directly to the shared photo directory on the web host and delete the photo file from the local system:

  •  
  •  
  •  
  •  
  •  
$ for f in * ; do    convert $f -scale 33% tmp/$f    scp -i seth_web tmp/$f [email protected]:~/public_html    trash tmp/$f ;  done

After finishing each file processed by the for loop, your computer will automatically run three commands. This means that if you only process 10 photos in this way, you can save yourself 30 commands and save as much time.

Limit cycle

It is not always necessary to view every file. You may only want to process JPEG files in the example directory:

  •  
  •  
  •  
$ for f in *.jpg ; do convert $f -scale 33% tmp/$f ; done$ ls -m tmpcat.jpg, otago.jpg

Finished ls -m tmpcat.jpg, otago.jpg Or, you may need to repeat the operation a specific number of times instead of processing the file. The variables of the for loop are defined by any data you provide, so you can create a loop that iterates through the iteration numbers instead of the file:

  •  
  •  
  •  
  •  
  •  
  •  
$ for n in {0..4}; do echo $n ; done01234

More cycles

You now know enough to create your own loop. Before being satisfied with the cycle, use them on the copy of the file to be processed, and use as many commands with built-in protection as possible to prevent you from damaging the data and causing irreparable errors, such as accidentally renaming the entire file , File directories with the same name, overwriting each other.

For more advanced for loop topics, please continue reading.

Not all shells are Bash

The for keyword is built into the Bash shell. Many similar shells use the same keywords and syntax, but some shells (such as tcsh) use different keywords (such as foreach) instead.

In tcsh, the syntax is essentially similar, but stricter than Bash. In the following code example, do you not type the string foreach? In lines 2 and 3. It is a secondary reminder to remind you that you are still in the process of building a loop.

  •  
  •  
  •  
  •  
  •  
  •  
  •  
$ foreach f (*)foreach? file $fforeach? endcat.jpg: JPEG image data, EXIF standard 2.2design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlacedotago.jpg: JPEG image data, EXIF standard 2.2waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced

In tcsh, both foreach and end must appear on separate lines, so you cannot create a for loop on one line as you would with Bash and similar shells.

Use the find command to execute a for loop

In theory, you might find a shell that does not provide a for loop function, or you may just prefer to use other commands with additional functions.

The find command is another way to implement the for loop function because it provides several methods to define the range of files to be included in the loop and parallel processing options.

The find command is designed to help you find files on your hard drive. Its syntax is simple: you provide the path to the location to search and find all files and directories:

  •  
  •  
  •  
  •  
  •  
  •  
$ find .../cat.jpg./design_maori.png./otago.jpg./waterfall.png

You can filter the search results by adding a part of name:

  •  
  •  
  •  
$ find . -name "*jpg"./cat.jpg./otago.jpg

The advantage of find is that you can use the -exec flag to enter each file you find into the loop. For example, to shrink only the PNG photos in the sample catalog, do the following:

  •  
  •  
  •  
$ find . -name "*png" -exec convert {} -scale 33% tmp/{} \;$ ls -m tmpdesign_maori.png, waterfall.png

In the -exec clause, the bracket character {} represents any item being processed (in other words, any file that ends in PNG, one at a time). The -exec clause must be terminated with a semicolon, but Bash usually tries to use the semicolon on its own. Use a backslash (\;) to "escape" the semicolon so that find knows to treat the semicolon as its terminating character.

The find command is very good at its function, and sometimes it may be too good. For example, if you reuse it to find another photo-processed PNG file, some errors will occur:

  •  
  •  
  •  
  •  
$ find . -name "*png" -exec convert {} -flip -flop tmp/{} \;   convert: unable to open image `tmp/./tmp/design_maori.png':No such file or directory @ error/blob.c/OpenBlob/2643....

It seems that find finds all the PNG files-not only the files in the current directory (.), But also the files you processed before and placed in the tmp subdirectory. In some cases, you may want to search the current directory and all other directories within it (and all directories within it). It can be a powerful recursive processing tool, especially in complex file structures (for example, a directory of music artists that contains albums full of music files), but you can limit it with the -maxdepth option.

Only find PNG files in the current directory (excluding subdirectories):

  •  
$ find . -maxdepth 1 -name "*png"

To find and process files in the current directory and other subdirectory levels, increase the maximum depth by 1:

  •  
$ find . -maxdepth 2 -name "*png"

Its default value is to enter all subdirectories.

Small extension

The more times you use loops, the more time and effort you save, and the more tasks you can handle. You are just a user, but after a thoughtful cycle, you can make the computer do the hard work.

You can and should treat loops like any other command, so you can keep them handy when you need to repeatedly perform one or two operations on multiple files. However, it is also a legal way to do serious programming, so if you have to perform complex tasks on any number of files, please take some time to plan your workflow. If you can achieve your goal on a file, it is relatively simple to wrap the repeatable process in a for loop, and the only “programming” required is to understand how variables work and enough organization to separate unprocessed files from The processed files are separated. With just a few exercises, you can move from a Linux user to a Linux user who knows how to write loops!

 

Shell scripts about loops

Regardless of the computer language, loops are an unavoidable topic, and Shell is certainly no exception. The following summarizes some of the commonly used loop-related knowledge points in Shell scripts, novice friends can refer to.

for loop

The simplest loop in the shell script is the  for loop, and friends with basic programming should have used the for loop. The simplest for loop is as follows, you only need to write the variable value after in in order:

#!/bin/bash

for num in 1 2 3 4
do
    echo $num
done

If the content to be looped is a continuous letter or a continuous number in the alphabet, then you can write a script according to the following syntax:

#!/bin/bash

for x in {a..z}
do
    echo $x
done

while loop

In addition to for loops, Shell also provides  while loops. For other languages, if you have seen a for loop but not a while loop, then you must have learned a fake language.

In the while loop, each time the loop is executed, the condition is judged once to determine whether the loop should continue. In fact, when the number of loops is relatively small, the effect of the for loop is similar to that of the while loop, but if the number of loops is relatively large, such as 100,000 times, then the advantages of the while loop are reflected.

#!/bin/bash

n=1

while [ $n -le 4 ]
do
    echo $n
    ((n++))
done

Loop set loop

Like other high-level languages, loops can be nested within each other. For example, in the following example, we put another for loop in the while loop:

#!/bin/bash

n=1

while [ $n -lt 6 ]
do
    for l in {a..d}
    do
        echo $n$l
    done
    ((n++))
done

The result of this script execution should be 1a, 1b, 1c, 1d, 2a, 2b… 5d.

The content of the loop is changing

In the for loop we mentioned above, the values ​​to be assigned to the loop variables are listed in the list after in. But this flexibility is too poor, because in many cases, the value to be obtained by the loop variable is not fixed.

For example, there is a variable to get all users on the current system, but because each computer user is different, we simply can't write this variable to death.

In this case, we can use the ls command to list all users in the / home directory, and then use the loop variable to get them in sequence. The complete code is as follows:

#!/bin/bash

for user in `ls /home`
do
    echo $user
done

Of course, in addition to ls, Shell also supports other commands. For example, we can use the date command to obtain the current system time, and then print it out in turn:

$ for word in `date`
> do
>     echo $word
> done
Thu
Apr
9
08:12:09
CST
2020

Variable value check

When we use a while loop, we often need to determine whether the value of a variable is greater than or less than a certain number. Sometimes this number is also represented by another variable, then we need to judge whether the value of this variable is a number. There are three ways to judge:

#!/bin/bash

echo -n "How many times should I say hello? "
read ans

if [ "$ans" -eq "$ans" ]; then
    echo ok1
fi

if [[ $ans = *[[:digit:]]* ]]; then
    echo ok2
fi

if [[ "$ans" =~ ^[0-9]+$ ]]; then
    echo ok3
fi

The first method seems like nonsense, but in fact, it -eq can only be used to judge between values. If it is a string, the judgment fails, so this ensures that ans is a numeric variable.

The second method is to use Shell wildcards to judge variables.

The third method is more straightforward, using regular expressions to judge variables.

Let's look at an example directly:

#!/bin/bash

echo -n "How many times should I say hello? "
read ans

if [ "$ans" -eq "$ans" ]; then
  n=1
  while [ $n -le $ans ]
  do
    echo hello
    ((n++))
  done
fi

In this script, I pass the number of loops to the ans variable, and then the script prints hello a few times. In order to ensure that the content we pass in is a number, we use  if [ "$ans" -eq "$ans" ] sentences to judge. If we don't pass in a number, we will not enter the while loop.

Loop out text file content

If you want to loop through the contents of the text file in turn, you can do this:

#!/bin/bash

echo -n "File> "
read file
n=0

while read line; do
  ((n++))
  echo "$n: $line"
done < $file

Here, we use the read command to read the content of the text file into the file variable, and then use redirection (the last line of the above script) to pass the file content into the while loop for processing and then print it out.

Endless loop

Sometimes we need to do something forever, so we can use an infinite loop. To achieve this goal is very simple, just use it while true .

#!/bin/bash

while true
do
    echo -n "Still running at "
    date
    sleep 1
done

In the above script, it will be printed every 1 second  Still running at 具体时间 until you press Ctrl + C to terminate the script.

END

Published 25 original articles · praised 8 · 20,000+ views

Guess you like

Origin blog.csdn.net/boazheng/article/details/105446510