Shell脚本初学者指南2:For循环

时间:2020-01-09 10:43:51  来源:igfitidea点击:

如果我们想建立自己的极客信誉,请加入我们的shell脚本系列的第二部分。我们进行了一些更正,对上周的脚本进行了一些改进,以及有关针对未启动者的循环的指南。

重新访问datecp脚本

在我们的shell脚本指南的第一部分中,我们创建了一个脚本,该脚本将日期添加到文件名的末尾,然后将文件复制到备份目录。

Samuel Dionne-Riel在评论中指出,有一种更好的方式来处理我们的变量引用。

Arguments are space-separated in the bash shell, it will tokenize when there is a space in the resulted expanded command. In your script, cp  .$date_formatted will work as intended as long as the expanded variables do not have spaces in them. If you call your script this way: datecp "my old name" "my new name" the expansion will result in this command: cp my new name my old name.the_date which actually has 6 arguments.
To properly address this issue, the last line of the script should be: cp "" ".$date_formatted"

如我们所见,将我们的脚本行从以下位置更改:

cp -iv  .$date_formatted

至:

cp -iv “” “”.$date_formatted

在名称中包含空格的文件上使用脚本时,将解决此问题。塞缪尔(Samuel)还指出,从该站点(或者一般而言,Internet)复制和粘贴代码时,请确保将适当的破折号和引号替换为通常替换它们的印刷上更好的破折号和引号。还要做更多的工作以确保我们的代码对复制/粘贴更加友好。 ;-)

另一个评论者Myles Braithwaite决定扩展我们的脚本,以便日期显示在文件扩展名之前。所以代替

tastyfile.mp3.07_14_11-12.34.56

我们会得到这个:

tastyfile.07_14_11-12.34.56.mp3

最终对大多数用户来说更加方便。他的代码可在其GitHub页面上找到。让我们看看他使用什么来拆分文件名。

date_formatted=$(date +%Y-%m-%d_%H.%M%S)

file_extension=$(echo “″|awk -F . ‘{print $NF}')

file_name=$(basename  .$file_extension)
cp -iv  $file_name-$date_formatted.$file_extension

我已经更改了格式,但是我们可以看到Myles在第1行中声明了他的日期函数。但是在第2行中,他使用echo命令和脚本的第一个参数来输出文件名。他使用pipe命令获取该输出,并将其用作下一部分的输入。在管道之后,Myles调用awk命令,这是一个功能强大的模式扫描程序。使用-F标志,他告诉命令下一个字符(在空格之后)将定义字段分隔符。在这种情况下,那就是一个时期。

现在,awk看到一个名为Deliciousfile.mp3的文件由两个字段组成:tastyfile和mp3. 最后,他使用

‘{print $NF}'

显示最后一个字段。如果文件有多个句点,从而使awk看到多个字段,它将仅显示最后一个,即文件扩展名。

在第3行中,他为文件名创建了一个新变量,并使用basename命令引用$ 1中除文件扩展名之外的所有内容。这是通过使用basename并将其赋予$ 1作为其参数,然后添加一个空格和文件扩展名来完成的。由于引用第2行的变量,文件扩展名被自动添加。

tastyfile.mp3

然后变成

tastyfile

然后在最后一行,Myles组合了将按顺序输出所有内容的命令。注意,没有引用$ 2,这是脚本的第二个参数。该特定脚本将代替地将所述文件复制到当前目录中。出色的塞缪尔和迈尔斯!

运行脚本和$ PATH

我们还在"基础知识"文章中提到,默认情况下允许将arent脚本作为命令引用。也就是说,我们必须指向脚本的路径才能运行它:

./script
~/bin/script

但是,通过将脚本放在~/ bin /中,我们只需在任何地方键入它们的名称即可运行它们。

评论者花了一些时间来讨论这个方法的正确性,因为没有现代的Linux发行版默认会创建该目录。此外,默认情况下,也没有人将其添加到$ PATH变量中,这是脚本像命令一样运行所必需的。我有点困惑,因为在检查了$ PATH变量之后,注释者是正确的,但是调用脚本仍然对我有用。我发现了原因:许多现代Linux发行版都在用户主目录.profile中创建了一个特殊文件。

该文件由bash读取(除非用户主目录中存在.bash_profile),并且在底部有一个部分,它将~/ bin /文件夹添加到$ PATH变量(如果存在)中。因此,这个谜底被清除了。在本系列的其余部分中,我将继续在~/ bin /目录中放置脚本,因为它们是用户脚本,应该可以由用户运行。而且,看来我们真的不需要用手弄乱$ PATH变量就可以使事情工作。

用循环重复命令

让我们使用怪胎库中最有用的工具来处理重复性任务:循环。今天,讨论循环。

for循环的基本概述如下:

for VARIABLE in LIST; do

command1

command2

…

commandn

done

VARIABLE可以是任何变量,尽管通常习惯上使用小写字母i。 LIST是项目列表;我们可以指定多个项目(用空格分隔),指向外部文本文件或者使用星号(*)表示当前目录中的任何文件。列出的命令按约定缩进,因此更容易看到嵌套将循环放入循环中(以便我们可以在循环时循环)。

因为列表使用空格作为分隔符,所以空格表示移动到列表文件中的下一项,名称中的空格非常友好。现在,让我们继续处理没有空格的文件。让我们从一个简单的脚本开始,显示当前目录中的文件名。在~/ bin /文件夹中创建名为loopscript的新脚本。如果我们不记得该怎么做(包括将其标记为可执行文件并添加哈希爆炸技巧),请参阅我们的bash脚本编写基础知识文章。

其中输入以下代码:

for i in item1 item2 item3 item4 item5 item6; do

echo “$i”

done

运行脚本时,应该只获取这些列表项作为输出。

很简单,对吧?让我们看看如果我们稍微改变一下会发生什么。更改脚本,使其显示以下内容:

for i in *; do

echo “$i”

done

当我们在文件夹中运行此脚本时,应该获得包含其中的文件列表作为输出。

现在,让我们将echo命令更改为更有用的命令zip命令。即,将文件添加到存档中。并且,让我们混合一些参数!

for i in $@; do

zip archive “$i”

done

有新东西! $ @是$ 1 $ 2 $ 3 $ n的快捷方式。换句话说,它是我们指定的所有参数的完整列表。现在,观察当我用几个输入文件运行脚本时会发生什么。

我们可以看到我的文件夹中有哪些文件。我运行了带有六个参数的命令,并且每个文件都被添加到一个名为archive.zip的压缩存档中。容易吧?

For循环非常棒。现在,我们可以在文件列表上执行批处理功能。例如,我们可以将所有脚本参数复制到压缩的归档文件中,将原始文件移动到其他文件夹中,然后自动将该zip文件复制到远程计算机上。如果我们使用SSH设置密钥文件,则甚至不需要输入密码,甚至可以告诉脚本在上传后删除zip文件!