使用for循环和find命令自动对多个文件执行一组操作。
$ **for f in * ; **do
人们想要学习Unix shell的一个常见原因是释放批处理的功能。如果要对许多文件执行某些操作,一种方法是构造一个遍历这些文件的命令来实现。在编程术语中,这称为执行控制,最常见的示例之一是for循环。
for循环是一个配方,详细说明了您希望计算机对指定的每个数据对象(例如文件)执行什么操作。
经典的循环
Linux终端适用于Linux的7大终端仿真器用于Linux中进行数据分析的10个命令行工具立即下载:SSH备忘单高级Linux命令备忘单Linux命令行教程一个简单的循环是分析文件集合的循环。这本身可能不是一个有用的循环,但它是一种安全的方法,可以向您证明自己有能力分别处理目录中的每个文件。首先,通过创建目录并将一些文件的某些副本放入其中来创建一个简单的测试环境。一开始的时候使用任何文件都可以,但是以后的示例需要图形文件(例如JPEG、PNG或类似文件)。您可以使用文件管理器或在终端中创建文件夹并将文件复制到其中:
$ mkdir example
$ cp ~/Pictures/vacation/.{png,jpg} example
将目录更改为新文件夹,然后列出其中的文件以确认测试环境符合您的期望:
$ cd example
$ ls -1
cat.jpg
design_maori.png
otago.jpg
waterfall.png
在一个循环中逐个遍历每个文件的语法是:创建一个变量。然后定义您要变量循环通过的数据集。在这种情况下,请使用通配符循环浏览当前目录中的所有文件(通配符匹配所有内容)。然后以分号(;)终止此介绍性子句。
$ for f in ;
根据您的喜好,您可以选择按此处返回。在语法上完成之前,shell不会尝试执行循环。
接下来,定义您希望在每次循环迭代中发生的事情。为简单起见,请使用file命令获取有关每个文件的少量数据,这些数据由f变量表示(但是以$开头,告诉shell将变量的值替换为当前包含的变量):
do file $f ;
用另一个分号终止子句并关闭循环:
done
做完了按Return键可启动Shell循环遍历当前目录中的所有内容。for循环将每个文件一个一个地分配给变量f,然后运行命令:
$ 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
您也可以这样写:
$ 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
多行和单行格式对于您的外壳都是相同的,并且产生完全相同的结果。
一个实际的例子
这是一个循环如何对日常计算有用的实际示例。假设您有要发送给朋友的度假照片集。您的照片文件很大,太大而无法通过电子邮件发送,并且不便上传到您的照片共享服务。您想为照片创建较小的网络版本,但是您有100张照片,不想浪费时间一张一张地缩小每张照片。
首先,在Linux,BSD或Mac上使用包管理器安装ImageMagick命令。例如,在Fedora和RHEL上:
$ sudo dnf install ImageMagick
在Ubuntu或Debian上:
$ sudo apt install ImageMagick
在BSD上,使用端口或pkgsrc。在Mac上,使用Homebrew或MacPorts。
安装ImageMagick后,您将拥有一组用于对照片进行操作的新命令。
为您要创建的文件创建目标目录:
$ mkdir tmp
要将每张照片缩小到其原始大小的33%,请尝试以下循环:
$ for f in ; do convert $f -scale 33% tmp/$f ; done
然后在tmp文件夹中查看缩放后的照片。
您可以在循环中使用任意数量的命令,因此,如果您需要对一批文件执行复杂的操作,则可以将整个工作流放在for循环的do和done语句之间。例如,假设您要将每张处理过的照片直接复制到Web主机上的共享照片目录,并从本地系统中删除照片文件:
$ for f in ; do
convert $f -scale 33% tmp/$f
scp -i seth_web tmp/$f seth@example.com:~/public_html
trash tmp/$f ;
done
做完了对于for循环处理的每个文件,您的计算机将自动运行三个命令。这意味着,如果您仅以这种方式处理10张照片,则可以为自己节省30条命令,还会节省同样多的时间。
限制循环
并不一定总是要查看每个文件。您可能只想处理示例目录中的JPEG文件:
$ for f in .jpg ; do convert $f -scale 33% tmp/$f ; done
$ ls -m tmp
cat.jpg, otago.jpg
做完了 ls -m tmpcat.jpg,otago.jpg或者,您可能需要重复执行特定次数的操作,而不是处理文件。for循环的变量由您提供的任何数据定义,因此您可以创建一个循环访问迭代数字而不是文件的循环:
$ for n in {0..4}; do echo $n ; done
0
1
2
3
4
更多的循环
您现在已经足够了解创建自己的循环了。在对循环感到满意之前,请在要处理的文件副本上使用它们,并尽可能多地使用带有内置保护措施的命令,以防止您破坏数据并造成不可弥补的错误,例如意外重命名整个文件,相同名称的文件目录,彼此覆盖。
有关高级for循环主题,请继续阅读。
并非所有的shell都是Bash
for关键字内置在Bash shell中。许多相似的shell使用相同的关键字和语法,但是某些shell(例如tcsh)使用不同的关键字(例如foreach)来代替。
在tcsh中,语法本质上相似,但比Bash严格。在以下代码示例中,是否不键入字符串foreach?在第2行和第3行中。它是辅助提示,提醒您仍在构建循环的过程中。
$ foreach f ()
foreach? file $f
foreach? end
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
在tcsh中,foreach和end都必须单独出现在单独的行中,因此不能像使用Bash和类似的shell那样在一行上创建for循环。
使用find命令执行for循环
从理论上讲,您可能会发现一个不提供for循环函数的shell,或者您可能只是更喜欢使用带有附加功能的其他命令。
find命令是实现for循环功能的另一种方法,因为它提供了几种方法来定义要包含在循环中的文件范围以及并行处理选项。
find命令旨在帮助您在硬盘驱动器上查找文件。它的语法很简单:您提供要搜索的位置的路径,并找到所有文件和目录:
$ find .
.
./cat.jpg
./design_maori.png
./otago.jpg
./waterfall.png
你可以通过添加name的一部分来过滤搜索结果:
$ find . -name “jpg”
./cat.jpg
./otago.jpg
find的优点在于,可以使用-exec标志将找到的每个文件输入到循环中。例如,要仅缩小示例目录中的PNG照片,请执行以下操作:
$ find . -name “png” -exec convert {} -scale 33% tmp/{} \;
$ ls -m tmp
design_maori.png, waterfall.png
在-exec子句中,括号字符{}代表正在处理的任何项(换句话说,已定位的任何以PNG结尾的文件,一次一个)。-exec子句必须以分号终止,但是Bash通常尝试自行使用分号。使用反斜杠(\;)“转义”分号,以便find知道将分号视为其终止字符。
find命令非常擅长于其功能,有时它可能太好了。例如,如果重复使用它来查找另一个照片处理的PNG文件,则会出现一些错误:
$ 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.
…
似乎find找到了所有的PNG文件-不仅是当前目录(。)中的文件,还包括您之前处理过并放在tmp子目录中的文件。在某些情况下,您可能想要搜索当前目录以及其中的所有其他目录(以及其中的所有目录)。它可以是功能强大的递归处理工具,尤其是在复杂的文件结构中(例如,音乐艺术家的目录中包含充满音乐文件的专辑目录),但是您可以使用-maxdepth选项对其进行限制。
只查找当前目录下的PNG文件(不包括子目录):
$ find . -maxdepth 1 -name “png”
要在当前目录以及其他子目录级别中查找和处理文件,请将最大深度增加1:
$ find . -maxdepth 2 -name “*png”
它的默认值是进入所有子目录。