当前位置: 首页 > 科技观察

如何在Bash中编程:循环

时间:2023-03-15 22:18:42 科技观察

本文是Bash编程系列中的最后一篇,由三部分组成,旨在学习如何使用循环执行迭代操作。Bash是一种用于命令行和shell脚本的强大编程语言。本系列的所有三个部分都基于我的三集Linux自学课程,探索如何使用CLI在bash中编程。本系列的第一篇文章讨论了bash编程的一些简单命令行操作,例如使用变量和控制运算符。第二篇文章探讨了文件、字符串、数字等类型,以及在执行流程中提供控制逻辑的各种逻辑运算符,以及bash中的各种扩展。本文是第三篇(也是最后一篇)文章,旨在探讨循环在各种迭代操作中的使用,以及如何合理控制循环。循环我曾经使用过的每种编程语言都至少有两个循环结构来执行重复操作。我经常使用for循环,但我发现while和until循环也很有用。for循环我的理解是bash中实现的for命令比大多数语言更灵活,因为它可以处理非数值;相比之下,标准C语言等for循环只能处理数值。for命令的Bash版本的基本结构非常简单:forVarinlist1;做清单2;done解释:“对list1中的每一个值,将$Var设置为该值,并使用该值执行list2中的程序语句;list1中的所有值执行完毕后,整个循环结束,循环退出。”list1中的值可以是简单的显式字符串值,也可以是命令执行的结果(``包含命令执行结果,在本系列的第二篇文章中介绍)。我经常使用这种结构。为了测试它,make确保~/testdir仍然是当前工作目录(PWD)。要删除目录中的所有内容,请参见这个简单的for循环示例,它明确地写出值列表。这个列表是字母和数字的混合——但是不要'忘记在bash中所有变量都是字符串或者可以被视为字符串。[student@studentvm1testdir]$rm*[student@studentvm1testdir]$forIinabcd1234;做echo$I;doneabcd1234给变量起一个更有意义的名字,成为上一个版本的高级版本:[student@studentvm1testdir]$forDeptin"HumanResources"SalesFinance"InformationTechnology"EngineeringAdministrationResearch;做echo"Department$Dept";doneDepartmentHumanResourcesDepartmentSalesDepartmentFinanceDepartmentInformationTechnologyDepartmentEngineeringDepartmentAdministrationResearchcreatesseveraldirectoryWorkingonInformation):[student@studentvm1testdir]$forDeptin"HumanResources"SalesFinance"InformationTechnology"EngineeringAdministrationResearch;doecho"在部门$Dept工作";mkdir"$部门";完成部门人力资源部门工作部门销售工作部门财务工作部门信息技术工作部门工程部门工作部门行政onWorkingonDepartmentResearch[student@studentvm1testdir]$lltotal28drwxrwxr-x2student学生4096Apr815:45Administrationdrwxrwxr-x2student学生4096Apr815:45Engineeringdrwxrwxr-x2student学生4096Apr815:45Financedr-wxrwxrx2student学生4096Apr815:45'人力资源'drwxrwxr-x2student学生4096Apr815:45'信息技术'drwxrwxr-x2student学生4096Apr815:45Researchdrwxrwxr-x96815:45mkdir语句中Sales的$Dept变量必须用引号引起来;否则,名称中有空格的(如InformationTechnology)将被视为两个单独的目录。我一直信奉的一条实用规则:所有文件和目录都应该是一个单词(中间没有空格)。虽然大多数现代操作系统可以处理名称之间带有空格的名称,但系统管理员需要花费额外的精力来确保脚本和CLI程序正确处理这些特殊情况。(尽管它们很烦人,但一定要考虑它们,因为你永远不知道你将拥有什么文件。)再次删除~/testdir下面的所有东西—再运行一次下面的命令:[student@studentvm1testdir]$rm-rf*;lltotal0[student@studentvm1testdir]$forDeptinHuman-ResourcesSalesFinanceInformation-TechnologyEngineeringAdministrationResearch;doecho"在部门$Dept工作";mkdir"$部门";完成在部门人力资源上工作在部门销售上工作在部门财务上工作在部门信息技术上工作在部门工程上工作在部门行政上工作在部门研究上工作[student@studentvm1testdir]$lltotal28drwxrwxr-x2student学生4096Apr815:52Administrationdrwxrwxr-x2学生学生40964月8日15:52工程drwxrwxr-x2学生学生40964月8日15:52金融drwxrwxr-x2学生学生40964月8日15:52人力资源drwxrwxr-x2学生学生40964月8日15:52信息技术drwxrwxr-x2学生学生4096Apr815:52Researchdrwxrwxr-x2学生学习nt4096Apr815:52Sales假设需要列出Linux机器上的所有RPM包,并为每个包附加一个简短的描述。我在北卡工作的时候,遇到过这种需求。由于当时开源还没有被国家“批准”,而我只在台式机上使用Linux,我的技术娴熟的老板(PHB)让我列出我电脑上安装的所有软件,以便他们“批准”一个特例。你如何实现它?一种方法是,已知rpm–qa命令可提供RPM包的完整描述,包括白痴老板想要的:软件名称和概要描述。让我们逐步执行以获得最终结果。首先,列出所有RPM包:[student@studentvm1testdir]$rpm-qaperl-HTTP-Message-6.18-3.fc29.noarchperl-IO-1.39-427.fc29.x86_64perl-Math-Complex-1.59-429。fc29.noarchlua-5.3.5-2.fc29.x86_64java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64util-linux-2.32.1-1.fc29.x86_64libreport-fedora-2.9.7-1.fc29.x86_64rpcbind-1.2.5-0.fc29.x86_64libsss_sudo-2.0.0-5.fc29.x86_64libfontenc-1.1.3-9.fc29.x86_64使用sort和uniq命令排序并打印列表重新安装后的结果(一些已安装的RPM包具有相同的名称):[student@studentvm1testdir]$rpm-qa|排序|uniqa2ps-4.14-39.fc29.x86_64aajohan-comfortaa-fonts-3.001-3.fc29。noarchabattis-cantarell-fonts-0.111-1.fc29.noarchabiword-3.0.2-13.fc29.x86_64abrt-2.11.0-1.fc29.x86_64abrt-addon-ccpp-2.11.0-1.fc29.x86_64abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64abrt-addon-kerneloops-2.11.0-1.fc29.x86_64abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64abrt-addon-vmcore-2.11.0-1.fc29.x86_64上面的命令得到了想要的RPM列表,所以可以将这个列表作为循环的输入信息,循环最终会打印出每个RPM包的详细信息:[student@studentvm1testdir]$forRPMin`rpm-qa|排序|独特的;做rpm-qi$RPM;done这段代码产生冗余信息当循环结束后,下一步就是提取白痴老板需要的信息。因此,添加一个egrep命令来搜索匹配^Name或^Summary的行。插入符号(^)表示行的开头,整个命令表示显示以Name或Summary开头的所有行。[student@studentvm1testdir]$forRPMin`rpm-qa|排序|独特的;做rpm-qi$RPM;完成|egrep-i"^Name|^Summary"Name:a2psSummary:ConvertstextandothertypesoffilestoPostScriptName:aajohan-comfortaa-fontsSummary:ModernstyletruetypefontName:abattis-cantarell-fontsSummary:Humanistsansserif字体名称:abiwordSummary:WordprocessingprogramName:abrtSummary:自动错误检测和报告工具RPM-summary.txt这个命令行程序在一行中使用了管道、重定向和for循环。它将CLI程序的结果重定向到一个文件,该文件可用于电子邮件或在其他地方作为输入。这种一步一个脚印构建程序的过程,可以让你看到每一步的结果,从而保证整个程序按照你的预期进行,输出你想要的结果。白痴老板最终列出了超过1900个不同的RPM包,我严重怀疑有人甚至读过。我给了他们他们想要的东西,但从未从他们那里听说过任何关于RPM包的消息。其他循环Bash中还有两种其他类型的循环结构:while和until结构,两者在语法和功能上都相似。这些循环结构的基本语法很简单:while[expression];做清单;done逻辑解释:当表达式(expression)的结果为真时,执行程序语句列表。当表达式的计算结果为false时,退出循环。直到[表达式];做清单;done逻辑解释:执行程序语句列表,直到表达式的结果为真。当表达式的计算结果为真时,退出循环。While循环while循环用于在逻辑表达式的计算结果为真时执行一系列程序语句。假设你的PWD仍然是~/testdir。while循环的最简单形式是永远运行的while循环。下面形式的条件语句总是返回true。您也可以用简单的1代替true得到相同的结果,但这解释了true表达式的用法。[student@studentvm1测试目录]$X=0;而[真];做echo$X;X=$((X+1));完成|head0123456789[student@studentvm1testdir]$既然你已经学会了这部分知识,那就让它更有用吧。首先,将$X的值设置为0,以防止变量$X在执行前一个程序或CLI命令后有剩余值。然后,由于逻辑表达式[true]的结果始终为1,即true,do和done之间的程序指令列表将继续执行——或者直到您按下Ctrl+C或向程序发送信号编号2。那些程序指令是算术扩展,用于打印变量$X的当前值并加1。《系统管理员的 Linux 哲学》的宗旨之一就是追求优雅,而实现优雅的方法之一就是简化。您可以使用运算符++简化此程序。在第一个示例中,打印变量的当前值,然后递增变量的值。你可以在变量后面加一个++来表示这个逻辑:[student@studentvm1~]$X=0;而[真];做echo$((X++));完成|head0123456789现在删除最后一个|程序的头部,然后运行一次。在下面的版本中,变量在打印值之前递增。这是通过在变量前添加++运算符来实现的。您看得出来差别吗?[student@studentvm1~]$X=0;而[真];做echo$((++X));完成|head123456789您已将变量值的打印和自动递增减少到单个语句。与++运算符一样,还有--运算符。当它循环到某个数字时,您需要一种方法来终止循环。用数字比较表达式替换真实表达式来实现这一点。这是一个循环到5并终止的程序。在下面的示例代码中,您可以看到-le是“小于或等于”的数字逻辑运算符。整个语句的意思:只要$X的值小于等于5,循环就会继续运行。当$X递增到6时,循环终止。[student@studentvm1~]$X=0;while[$X-le5];做echo$((X++));done012345[student@studentvm1~]$Until循环until命令与while命令非常相似。不同之处在于它循环直到逻辑表达式的计算结果为真。看看这个结构的最简单形式:[student@studentvm1~]$X=0;直到错误;做echo$((X++));完成|head0123456789[student@studentvm1~]$它使用逻辑比较表达式计数到特定值:[student@studentvm1~]$X=0;直到[$X-eq5];做echo$((X++));done01234[student@studentvm1~]$X=0;直到[$X-eq5];做echo$((++X));done12345[student@studentvm1~]$总结本系列探索了许多用于构建Bash命令行程序和shell脚本的强大工具。但这只是您可以使用Bash做的许多很酷的事情的冰山一角,这取决于您。我发现学习Bash编程的最好方法就是动手去做。以一个需要多个Bash命令的简单项目为例,编写一个CLI程序。系统管理员做很多适合CLI编程的工作,所以我相信您可以轻松找到要自动化的任务。许多年前,尽管我熟悉其他shell语言和Perl,但我还是决定使用Bash来完成所有系统管理员自动化任务。我发现,有时稍微搜索一下,我就可以在Bash中实现我需要的一切。