bash 脚本编写
人们想要学习Unix shell的一个常见原因是释放批处理的功能。 如果要对多个文件执行某些操作,一种方法是构造一个遍历这些文件的命令。 在编程术语中,这称为执行控制,最常见的示例之一是for循环。
for循环是一个配方,详细说明了您希望计算机对指定的每个数据对象(例如文件)采取什么操作。
经典的循环
$
mkdir example
$
cp ~
/ Pictures
/ vacation
/* .
{ png,jpg
} example
将目录更改为新文件夹,然后列出其中的文件以确认测试环境符合您的期望:
$
cd example
$
ls
-1
cat.jpg
design_maori.png
otago.jpg
waterfall.png
在一个循环中循环遍历每个文件的语法是:创建一个变量(例如,对于文件为f )。 然后定义您希望变量循环通过的数据集。 在这种情况下,请使用*通配符循环浏览当前目录中的所有文件( *通配符匹配所有内容 )。 然后以分号( ; )结束此介绍性子句。
$ for f in * ;
根据您的喜好,您可以选择按此处返回 。 在语法上完成之前,shell不会尝试执行循环。
接下来,定义您希望在每次循环迭代中发生的事情。 为简单起见,请使用file命令获取有关每个文件的少量数据,这些数据由f变量表示(但前面带有$表示外壳将变量的值换成当前包含的变量):
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
多行和单行格式对于您的Shell都是相同的,并且会产生完全相同的结果。
一个实际的例子
这是一个循环如何对日常计算有用的实际示例。 假设您有要发送给朋友的度假照片集。 您的照片文件很大,因此太大而无法通过电子邮件发送,因此不便上传到您的照片共享服务 。 您想为照片创建较小的网络版本,但是您有100张照片,不想浪费时间一张一张地缩小每张照片。
首先,在Linux,BSD或Mac上使用包管理器安装ImageMagick命令。 例如,在Fedora和RHEL上:
$ sudo dnf install ImageMagick
在Ubuntu或Debian上:
$ sudo apt install ImageMagick
在BSD上,使用ports或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
或者,您可能需要重复执行特定次数的操作,而不是处理文件。 for循环的变量由您提供的任何数据定义,因此您可以创建一个循环访问数字而不是文件的循环:
$
for n
in
{
0 ..
4
} ;
do
echo
$n ;
done
0
1
2
3
4
更多循环
您现在已经足够了解创建自己的循环了。 在对循环感到满意之前,请在要处理的文件副本上使用它们,并尽可能多地使用带有内置保护措施的命令,以防止您破坏数据和犯不可修复的错误,例如意外重命名整个文件。相同名称的文件目录,彼此覆盖。
有关高级for循环主题,请继续阅读。
并非所有的炮弹都是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循环功能的外壳,或者您可能只是更喜欢使用带有附加功能的其他命令。
find命令是实现for循环功能的另一种方法,因为它提供了几种方法来定义循环中要包含的文件的范围以及并行处理的选项。
find命令旨在帮助您在硬盘驱动器上查找文件。 它的语法很简单:您提供要搜索的位置的路径,并找到所有文件和目录:
$
find .
.
.
/ cat.jpg
.
/ design_maori.png
.
/ otago.jpg
.
/ waterfall.png
您可以通过添加部分名称来过滤搜索结果:
$
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"
它的默认值是进入所有子目录。
循环娱乐和获利
使用循环越多,节省的时间和精力就越多,可以处理的任务也就越大。 您只是一个用户,但是经过深思熟虑的循环,您可以使计算机完成艰苦的工作。
您可以并且应该像对待其他任何命令一样对待循环,以便在需要对多个文件重复执行一个或两个操作时可以将其放在手边。 但是,它也是进行认真编程的合法途径,因此,如果您必须对任意数量的文件执行复杂的任务,请抽出一些时间来计划工作流程。 如果可以在一个文件上实现目标,那么将该可重复过程包装在for循环中相对简单,并且唯一需要的“编程”是了解变量的工作方式以及足够的组织以将未处理的文件与已处理的文件分开。 只需做一些练习,您就可以从Linux用户转到知道如何编写循环的Linux用户,因此,请走出去,让您的计算机为您服务!
翻译自: https://opensource.com/article/19/6/how-write-loop-bash
bash 脚本编写