当前位置: 首页 > Linux

13种Shell逻辑和算术,能写出5种你就赢了!

时间:2023-04-06 06:44:13 Linux

现代bash版本相对于原始Bourneshell的最大改进之一是算法。早期版本的shell没有内置的算术函数,即使将变量加1也必须调用单独的程序来完成。1、算术方法一:只要$(())是整数运算,$(())的算术表达式中可以使用所有标准运算符。还有一个额外的运算符:**可以用于取幂,如下所示:COUNT=$((COUNT+5+MAX*2))或:MAX=$((2**8))$(())空格在表达式中不是必需的,但在运算符和操作数周围放置空格也无妨(但**必须写在一起)。但是=两边不能有空格,这和bash变量赋值的规则是一样的。如果你这样写:COUNT=$((COUNT+5))#注意=号两边多了个空格,不是你想的那样!然后,bash将尝试运行一个名为COUNT的程序,其中=作为它的第一个参数,$COUNT和5的总和作为它的第二个参数。切记,作业号两边不要有空格!另一个怪癖是通常在shell变量之前表示值(例如$COUNT或$MAX)的$符号在双括号内不需要。例如,我们可以这样写:$((COUNT+5+MAX*2))shell变量前没有$符号,实际上是把外面的$作用于整个表达式。但是如果使用位置参数(比如$2),$还是必不可少的,因为只有这样才能把位置参数和数值常量(比如2)区分开来。下面是一个例子。COUNT=$((COUNT+$2+OFFSET))也可以用逗号运算符构成级联赋值,如下图:echo$((X+=5,Y*=3))这个表达式进行了两次赋值操作,然后第二个子表达式的结果由echo显示(因为逗号运算符返回其第二个操作数的值)。2、算术方法二:除了使用$(())进行算术运算,let还可以使用let语句,如下:letCOUNT=COUNT+5与$(())相同,不需要使用变量符号时使用$。但是,当我们需要使用let进行COUNT=$((COUNT+5+MAX*2))格式的运算时,需要使用引号'',如下:letCOUNT+='5+MAX*2'let语句和$(())语法的另一个重要区别是它们如何处理空白字符(spacecharacters)。对于let语句,引号或赋值运算符(=)和其他运算符周围不能有空格。运算符和操作数必须放在一起形成一个词。下面两种写法都可以。leti=2+2let"i=2+2"$(())语法要轻松得多,允许在双括号内出现各种空白字符。这种写法不易出错,代码可读性更高,是我们执行bash整数运算的首选方式。3、bash中的赋值运算符4、条件判断if条件分支。逻辑分支是任何语言都会遇到的问题。bash中和其他语言类似,if用于条件判断,如下:if[$#-lt3]thenprintf"%b""错误。参数不足。\n"printf"%b""用法:myscriptfile1opfile2\n"exit1fi或:if(($#<3))thenprintf"%b""错误。参数不足。\n"printf"%b""用法:myscriptfile1opfile2\n"exit1fi下面是一个完整的带有elif(bash中的else-if)和else子句的if语句。如下:if(($#<3))thenprintf"%b""Error.Notenougharguments.\n"printf"%b""usage:myscriptfile1opfile2\n"exit1elif(($#>3))thenprintf"%b""错误。参数太多。\n"printf"%b""用法:myscriptfile1opfile2\n"exit2elseprintf"%b""参数计数正确。继续...\n"fi关于if,我们有两个问题需要了解,即:if语句的基本结构,if表达式的不同语法(括号或方括号,运算符或选项)5.if的基本结构遵循bash手册页if语句的一般形式如下。如果列出;然后列出;[elif列表;然后列出;]...[其他列表;]fi[和]用于分隔语句的可选部分(例如,某些if语句没有else子句)。让我们首先看一下没有任何可选部分的if语句。if语句的最简单形式如下所示。如果列出;然后列出;fi在bash中,和换行符一样,分号的作用是结束一条语句。我们可以用分号将解决方案部分中的示例塞进更少的行,但使用换行符可读性更强。then列表的存在似乎是有道理的,如果if条件为真,则执行其中的语句(我们也可以从其他编程语言中猜测)。但是if列表呢?它不应该是一个if表达式吗?是的,但这是shell,一个命令处理器。它的主要任务是执行命令。因此,if之后的列表就是放置命令列表的地方。你可能会问,是什么决定了分支的去向(then子句还是else子句)?答案是列表中最后一个命令的返回值。让我们用一个有点奇怪的例子来说明这一点。如下:$cattrythis.sh//查看脚本内容,如下图ifls;密码;裁谈会$1;thenechosuccessechofailedfi//执行脚本并传递一个参数$bash./trythis.sh/tmpinthisstrange在脚本中,shell在选择分支之前会执行3个命令(ls、pwd、cd),其中cd命令的参数是调用脚本时提供的第一个命令行参数。如果没有提供参数,它只是简单地cds回到主目录。结果会怎样?你可以自己试试。最后,显示“success”还是“failed”取决于cd命令是否执行成功。在示例中,cd是if语句的命令列表中的最后一个命令。如果cd失败,则转到else子句;但如果成功,它会选择then子句。6、if中的[]和(())我们一起来看看下面的例子:iftest$#-lt3thenechotryagain。fi前面提到过,if后面的列表是一个命令列表,虽然这不是命令列表,但是你看到里面有没有至少类似于单个shell命令的东西(内置命令test接受参数并比较它们的值)?我们在本章开头给出的第一个示例中的开头if[$#-lt3]看起来很像测试命令。这是因为[实际上只是同一命令的不同名称。(调用[还需要]作为可读性和美观性的最后一个参数。)因此,对于这种语法,if语句中的表达式实际上只是一个仅包含单个命令(测试命令)的列表。在早期的Unix中,test是一个独立的可执行文件,[只是指向该文件的链接。两者现在仍然作为可执行文件存在,但bash也将它们作为内置函数实现。那么if(($#<3))是什么意思呢?双括号是一种复合命令。由于它计算其中的算术表达式,因此它在if语句中很有用。这是一个相对较新的bash改进,特别适用于带有if语句的场合。可用于if语句的两种语法之间的一个重要区别是测试的表达方式和它可以测试的对象的种类。双括号仅限于算术表达式,方括号也可以测试文件特性,但后者的算术测试语法远不如前者方便,尤其是当表达式被括号分成几个子表达式时。我们在使用[]的时候一定要注意一定要有空格,如下图:if[-d"/opt/"]7.测试文件的特点为了提高脚本的健壮性,要在读取输入文件前先检查文件是否存在;另外,我还想在写输出文件前确认是否有写权限,在cd切换目录前检查是否有这个目录。这些如何在bash脚本中实现?如下:#!/usr/bin/envbash#示例文件:checkfile#DIRPLACE=/tmpINFILE=/home/yucca/amazing.dataOUTFILE=/home/yucca/more.resultsif[-d"$DIRPLACE"]//判断目录是否存在然后cd$DIRPLACEif[-e"$INFILE"]//判断文件是否存在然后if[-w"$OUTFILE"]//判断文件是否有写权限然后doscience<"$INFILE">>"$OUTFILE"elseecho"cannotwriteto$OUTFILE"fielseecho"cannotreadfrom$INFILE"fielseecho"cannotcdinto$DIRPLACE"fi将所有文件名引用放在引号中以防止路径名包含空格。在上面的例子中,我们用来测试文件是否是一个目录(-d),文件是否存在(-e),文件是否有写权限(-w),我们还可以测试一些其他的文件特征,的其中有3个属性使用二元运算符(接受两个文件名)。FILE1-ntFILE2是否更新(检查文件的修改时间)。现有文件比不存在的文件“更新”。FILE1-otFILE2是否更旧。同样,不存在的文件比现有文件“旧”。FILE1-efFILE2有相同的设备和inode号(即使被不同的链接指向,也被认为是同一个文件)。之前使用的-e、-d、-w都是一元运算符,形式为optionfilename,例如if[-emyfile]8.测试多个特征。在我们测试每个特性之前,我们使用单个if语句,那么当我们测试多个特性时,我们是否必须嵌套if语句?使用-a(逻辑与)和-o(逻辑或)运算符将多个测试条件组合成一个表达式。例如:if[-r$FILE-a-w$FILE]这个if语句测试指定的文件是否可读可写。测试的时候为什么不加-e呢?因为所有文件测试条件都隐式测试文件是否存在,所以文件可读性测试不需要文件存在。如果文件不存在,自然是不可读的。这些逻辑运算符(-a用于AND,-o用于OR)可用于所有测试条件,并不限于文件测试。多个AND/OR可以出现在同一语句中。您可能需要使用括号来获得正确的优先级,例如a和(b或c),但请始终记住在它们前面加上反斜杠或将它们放在引号中以去除它们的特殊含义。如下:if[-r"$FN"-a\(-f"$FN"-o-p"$FN"\)]9.测试字符串属性你想在使用字符串之前检查它们的值。这些字符串可以是用户输入、读入的文件或传入脚本的环境变量。如何用bash脚本实现它?您可以在if语句中使用单方括号中的test命令来执行一些简单的测试,包括检查变量是否包含文本以及两个变量中的字符串是否相同。如下脚本所示:#使用命令行参数VAR="$1"##if["$VAR"]这种形式通常可以,但不是很好的写法,加上-n会更清楚if[-n"$VAR"]thenechohastextelseechozerolengthfiif[-z"$VAR"]thenechozerolengthelseechohastextfi有两种类型的长度为0的变量:设置为空字符串的变量和不存在。示例中的测试没有区分这两种情况。它只关心变量中是否有字符。将$VAR放在引号中很重要,否则测试会被一些奇怪的用户输入干扰。如果$VAR的值为x-a7-lt5且不使用引号,则以下语句:if[-z$VAR]变为(变量扩展后):if[-zx-a7-lt5]10.相等性测试您想要检查两个shell变量是否相等,但是有两个测试运算符:-eq和=(或==)。我应该使用哪个?您需要的比较类型决定了要使用的运算符。如果要进行数值比较,可以使用-eq运算符。如果要进行字符串比较,请使用=(或==)运算符。下面,我们通过一个简单的脚本示例进行演示,如下:##普通话字符串与值比较#VAR1="05"VAR2="5"printf"%s""他们-eq是否相等?"if["$VAR1"-eq"$VAR2"]thenechoYESelseechoNOfiprintf"%s""dothey=asequal?"if["$VAR1"="$VAR2"]thenechoYESelseechoNOfiprintfif,我们运行脚本,会得到如下结果:$./scriptnamethey-eqasequal?是的,他们=一样吗?NO$虽然两个变量的值是相等的(5),但是从字符的角度来看,前导字符0和空白字符就意味着两个字符串不一样。=和==都可以使用,但是=符合POSIX标准并且更便携。使用if,我们可以在脚本中进行分支判断。但对于系统而言,循环与分支一样是常见的需求。所以Shell也支持循环操作11.循环一段时间算术条件,使用while循环:while((COUNT