perl数组排序,perl的排序
使用sort函数对perl数组进行排序,例如:@sorted_array = sort @array;
使用map函数将数组元素求和,例如:$sum = 0; $sum += $_ foreach @array;
怎样用 Bash 编程:循环

本文是Bash编程系列三篇中的最后一篇,来学习使用循环执行迭代的操作。--DavidBoth(作者)
Bash是一种强大的用于命令行和shell脚本的编程语言。本系列的三部分都是基于我的三集Linux自学课程写的,探索怎么用CLI进行bash编程。
本系列的第一篇文章讨论了bash编程的一些简单命令行操作,如使用变量和控制操作符。第二篇文章探讨了文件、字符串、数字等类型和各种各样在执行流中提供控制逻辑的的逻辑运算符,还有bash中不同种类的扩展。本文是第三篇(也是最后一篇),意在考察在各种迭代的操作中使用循环以及怎么合理控制循环。
循环我使用过的所有编程语言都至少有两种循环结构来用来执行重复的操作。我经常使用for循环,然而我发现while和until循环也很有用处。
for循环我的理解是,在bash中实现的for命令比大部分语言灵活,因为它可以处理非数字的值;与之形成对比的是,诸如标准C语言的for循环只能处理数字类型的值。
Bash版的for命令基本的结构很简单:
forVarinlist1;dolist2;done
解释一下:“对于list1中的每一个值,把$Var设置为那个值,使用该值执行list2中的程序语句;list1中的值都执行完后,整个循环结束,退出循环。”list1中的值可以是一个简单的显式字符串值,也可以是一个命令执行后的结果(``包含其内的命令执行的结果,本系列第二篇文章中有描述)。我经常使用这种结构。
要测试它,确认~/testdir仍然是当前的工作目录(PWD)。删除目录下所有东西,来看下这个显式写出值列表的for循环的简单的示例。这个列表混合了字母和数字—但是不要忘了,在bash中所有的变量都是字符串或者可以被当成字符串来处理。
[student@studentvm1testdir]$rm*[student@studentvm1testdir]$forIinabcd1234;doecho$I;doneabcd1234
给变量赋予更有意义的名字,变成前面版本的进阶版:
[student@studentvm1testdir]$forDeptin"HumanResources"SalesFinance"InformationTechnology"EngineeringAdministrationResearch;doecho"Department$Dept";doneDepartmentHumanResourcesDepartmentSalesDepartmentFinanceDepartmentInformationTechnologyDepartmentEngineeringDepartmentAdministrationDepartmentResearch
创建几个目录(创建时显示一些处理信息):
[student@studentvm1testdir]$forDeptin"HumanResources"SalesFinance"InformationTechnology"EngineeringAdministrationResearch;doecho"WorkingonDepartment$Dept";mkdir"$Dept";doneWorkingonDepartmentHumanResourcesWorkingonDepartmentSalesWorkingonDepartmentFinanceWorkingonDepartmentInformationTechnologyWorkingonDepartmentEngineeringWorkingonDepartmentAdministrationWorkingonDepartmentResearch[student@studentvm1testdir]$lltotal28drwxrwxr-x2studentstudent4096Apr815:45Administrationdrwxrwxr-x2studentstudent4096Apr815:45Engineeringdrwxrwxr-x2studentstudent4096Apr815:45Financedrwxrwxr-x2studentstudent4096Apr815:45'HumanResources'drwxrwxr-x2studentstudent4096Apr815:45'InformationTechnology'drwxrwxr-x2studentstudent4096Apr815:45Researchdrwxrwxr-x2studentstudent4096Apr815:45Sales
在mkdir语句中$Dept变量必须用引号包裹起来;否则名字中间有空格(如InformationTechnology)会被当做两个独立的目录处理。我一直信奉的一条实践规则:所有的文件和目录都应该为一个单词(中间没有空格)。虽然大部分现代的操作系统可以处理名字中间有空格的情况,但是系统管理员需要花费额外的精力去确保脚本和CLI程序能正确处理这些特例。(即使它们很烦人,也务必考虑它们,因为你永远不知道将拥有哪些文件。)
再次删除~/testdir下的所有东西—再运行一次下面的命令:
[student@studentvm1testdir]$rm-rf*;lltotal0[student@studentvm1testdir]$forDeptinHuman-ResourcesSalesFinanceInformation-TechnologyEngineeringAdministrationResearch;doecho"WorkingonDepartment$Dept";mkdir"$Dept";doneWorkingonDepartmentHuman-ResourcesWorkingonDepartmentSalesWorkingonDepartmentFinanceWorkingonDepartmentInformation-TechnologyWorkingonDepartmentEngineeringWorkingonDepartmentAdministrationWorkingonDepartmentResearch[student@studentvm1testdir]$lltotal28drwxrwxr-x2studentstudent4096Apr815:52Administrationdrwxrwxr-x2studentstudent4096Apr815:52Engineeringdrwxrwxr-x2studentstudent4096Apr815:52Financedrwxrwxr-x2studentstudent4096Apr815:52Human-Resourcesdrwxrwxr-x2studentstudent4096Apr815:52Information-Technologydrwxrwxr-x2studentstudent4096Apr815:52Researchdrwxrwxr-x2studentstudent4096Apr815: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_64lt;snipgt;
用sort和uniq命令对列表进行排序和打印去重后的结果(有些已安装的RPM包具有相同的名字):
[student@studentvm1testdir]$rpm-qa|sort|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_64lt;snipgt;
以上命令得到了想要的RPM列表,因此你可以把这个列表作为一个循环的输入信息,循环最终会打印每个RPM包的详细信息:
[student@studentvm1testdir]$forRPMin`rpm-qa|sort|uniq`;dorpm-qi$RPM;done
这段代码产出了多余的信息。当循环结束后,下一步就是提取出白痴老板需要的信息。因此,添加一个egrep命令用来搜索匹配^Name或^Summary的行。脱字符(^)表示行首,整个命令表示显示所有以Name或Summary开头的行。
[student@studentvm1testdir]$forRPMin`rpm-qa|sort|uniq`;dorpm-qi$RPM;done|egrep-i"^Name|^Summary"Name:a2psSummary:ConvertstextandothertypesoffilestoPostScriptName:aajohan-comfortaa-fontsSummary:ModernstyletruetypefontName:abattis-cantarell-fontsSummary:HumanistsansseriffontName:abiwordSummary:WordprocessingprogramName:abrtSummary:Automaticbugdetectionandreportingtoollt;snipgt;
在上面的命令中你可以试试用grep代替egrep,你会发现用grep不能得到正确的结果。你也可以通过管道把命令结果用less过滤器来查看。最终命令像这样:
[student@studentvm1testdir]$forRPMin`rpm-qa|sort|uniq`;dorpm-qi$RPM;done|egrep-i"^Name|^Summary"gt;RPM-summary.txt
这个命令行程序用到了管道、重定向和for循环,这些全都在一行中。它把你的CLI程序的结果重定向到了一个文件,这个文件可以在邮件中使用或在其他地方作为输入使用。
这个一次一步构建程序的过程让你能看到每步的结果,以此来确保整个程序以你期望的流程进行且输出你想要的结果。
白痴老板最终收到了超过1900个不同的RPM包的清单,我严重怀疑根本就没人读过这个列表。我给了他们想要的东西,没有从他们嘴里听到过任何关于RPM包的信息。
其他循环Bash中还有两种其他类型的循环结构:while和until结构,两者在语法和功能上都类似。这些循环结构的基础语法很简单:
while[expression];dolist;done
逻辑解释:表达式(expression)结果为true时,执行程序语句list。表达式结果为false时,退出循环。
until[expression];dolist;done
逻辑解释:执行程序语句list,直到表达式的结果为true。当表达式结果为true时,退出循环。
While循环while循环用于当逻辑表达式结果为true时执行一系列程序语句。假设你的PWD仍是~/testdir。
最简单的while循环形式是这个会一直运行下去的循环。下面格式的条件语句永远以true作为返回。你也可以用简单的1代替true,结果一样,但是这解释了true表达式的用法。
[student@studentvm1testdir]$X=0;while[true];doecho$X;X=$((X+1));done|head0123456789[student@studentvm1testdir]$
既然你已经学了CLI的各部分知识,那就让它变得更有用处。首先,为了防止变量$X在前面的程序或CLI命令执行后有遗留的值,设置$X的值为0。然后,因为逻辑表达式[true]的结果永远是1,即true,在do和done中间的程序指令列表会一直执行—或者直到你按下Ctrl+C抑或发送一个2号信号给程序。那些程序指令是算数扩展,用来打印变量$X当前的值并加1.
《系统管理员的Linux哲学》的信条之一是追求优雅,实现优雅的一种方式就是简化。你可以用操作符++来简化这个程序。在第一个例子中,变量当前的值被打印出来,然后变量的值增加了。可以在变量后加一个++来表示这个逻辑:
[student@studentvm1~]$X=0;while[true];doecho$((X++));done|head0123456789
现在删掉程序最后的|head再运行一次。
在下面这个版本中,变量在值被打印之前就自增了。这是通过在变量之前添加++操作符实现的。你能看出区别吗?
[student@studentvm1~]$X=0;while[true];doecho$((++X));done|head123456789
你已经把打印变量的值和自增简化到了一条语句。类似++操作符,也有--操作符。
你需要一个在循环到某个特定数字时终止循环的方法。把true表达式换成一个数字比较表达式来实现它。这里有一个循环到5终止的程序。在下面的示例代码中,你可以看到-le是“小于或等于”的数字逻辑操作符。整个语句的意思:只要$X的值小于或等于5,循环就一直运行。当$X增加到6时,循环终止。
[student@studentvm1~]$X=0;while[$X-le5];doecho$((X++));done012345[student@studentvm1~]$
Until循环
until命令非常像while命令。不同之处是,它直到逻辑表达式的值是true之前,会一直循环。看一下这种结构最简单的格式:
[student@studentvm1~]$X=0;untilfalse;doecho$((X++));done|head0123456789[student@studentvm1~]$
它用一个逻辑比较表达式来计数到一个特定的值:
[student@studentvm1~]$X=0;until[$X-eq5];doecho$((X++));done01234[student@studentvm1~]$X=0;until[$X-eq5];doecho$((++X));done12345[student@studentvm1~]$
总结
本系列探讨了构建Bash命令行程序和shell脚本的很多强大的工具。但是这仅仅是你能用Bash做的很多有意思的事中的冰山一角,接下来就看你的了。
我发现学习Bash编程最好的方法就是实践。找一个需要多个Bash命令的简单项目然后写一个CLI程序。系统管理员们要做很多适合CLI编程的工作,因此我确信你很容易能找到自动化的任务。
很多年前,尽管我对其他的Shell语言和Perl很熟悉,但还是决定用Bash做所有系统管理员的自动化任务。我发现,有时稍微搜索一下,我可以用Bash实现我需要的所有事情。
via:https://opensource.com/article/19/10/programming-bash-loops
作者:DavidBoth选题:lujun9972译者:lxbwolf校对:wxy
本文由LCTT原创编译,Linux中国荣誉推出
点击“了解更多”可访问文内链接