1Tcl
控制结构允许程序根据不同的状态、条件和参数来选择不同的处理和执行路径,从而使代码具有更强的灵活性、健壮性和可读性。
Tcl 提供了 if、if/else、if/elseif、foreach、 for、while 和 switc等命令来管理控制结构。这些命令和其他语言如C语言的条件语句的作用相同。需要区别的是在 Tcl 中所有控制结构都是由相应的命令来实现,而 C 语言中则是一条控制语句。控制结构通常要求带有一个延迟执行命令体或者过程体,这个命令体需要用花括号括起来以加以界定。
1.1if/else 命令
if 命令根据表达式的结果来执行命令体:如果表达式结果为真,则执行命令体,否则会执行另外一个条件命令体(如果存在的话)。后面两个命令体(elseif 和 else)是可选的。
[语法] if { test expr 测试表达式 } {
body 1
} elseif {test expr 测试表达式} {
body2
} else {
test expr
}
1.语法中用以界定过程体的花括号一定要和 if 命令在同一行上!因为对 Tcl 来讲,换行符就是命令结束符,所以如果在 if 表达式后直接换行,写成:
if { test expr }
{
...
}
就会出错。Tcl 遇到换行后就认为命令结束,但找不到执行命令体,返回错误。其他的控制命令,还有以后的过程定义命令等等都存在这个问题。
但情况并不全部如此。当在一个花括号体内或者一个双引号体内换行的时候,解释器不认为是命令的结束,所以上面的语法中,我们只将执行命令体的第一个花括号(左括号)留在了 if 命令行和 else 命令行,然后另起一行书写执行命令体的过程语句,右括号也被单独放到了一行上。这样做是为了提高可读性和便于查错。
2.如果 if 后面还有 else/elseif 命令,则要留意 else/elseif 的位置。else/elseif 要跟在 if 执行命令体的后面一个花括号后,不能分行,要有空格间隔花括号和 else /elseif。
3.花括号括起的表达式、执行命令体或者其他内容相当于变量存在,所以前后与其他命令元素之前要有空格,否则 Tcl 会返回语法错误。
4.可以使用多个 elseif 来创建一连串的条件命令控制结构。
5.表达式支持变量替换和命令替换。
6.表达式的计算结果如果是”true”、”yes”和非零值就判断为真,如果结果
是”false”、”no”和零则判断为假。控制命令根据表达式结果来判断是否执行相应的执行命令体。
1.2for命令
for 命令和 C 语言的 for 语句相似。for 命令的语法格式为:
[语法]:for {start} {test expr} {next} {body
}
for 命令有四个变元,start 是预置条件或者初始化命令,告诉 for 命令起始执行条件。test
expr 是条件布尔表达式,以决定是否执行循环体 body,如果是真,则执行循环体,如果假则退出命令。如果表达式真,则在执行循环体后处理 next 命令,即 next 是一个后置命令执行体。前三个变元可以选择置空,而将相应的处理放到循环体 body 中去。
1.3while命令
while 命令格式为:
[语法]:while {test } {
body
}
while 命令和 for 命令非常相似。只要 test 为真,while 就执行循环体直到 test 变为假。for命令和 while 命令的主要区别是,在 while 循环体内你必须更改被检测的测试体 test 的值,否则如果值一直没有改变成假时,while 将无限的执行循环体。而 for 命令你可以将这种处理过程在 next 变元中显式给出。
1.4switch 命令
switch 命令通过将给定字符串与不同的匹配模式进行匹配从而选择执行多分支命令体。
switch 可基于模式匹配。命令格式为:
[语法]: switch [option] string {
pattern-1 {body1}
pattern-2 {body2}
pattern-n {bodyn}
}
说明:
1.option 主要有:
-exact 用精确匹配(默认);
-glob 用 glob 格式行模式匹配; -regexp 用 正则表达式模式匹配; -- 标记选项结束或者说明不用选项。
1.5eval命令
用于创建和运行tcl脚本的通用构造块。它接受任一多个参数,把它们用分隔符串接起来,然后把串接的结果作为一个tcl脚本处理。
set reset {
set a 0
set b 0
set c 0
}
...
eval $reset
又如:
set vars {a b c d}
eval unset $vars
串接 unset $vars 参数后结果为
unset {a b c d}
然后把以上语句作为一个tcl脚本执行,结果清除a b c d四个变量。
1.6source和return命令
读取一个文件,将其内容作为tcl脚本运行。一个参数,该参数指定要读取的文件名。例如:
source init.tcl改命令运行init.tcl文件的内容。可以用绝对路径或相对路径。source的返回值就是文件内容的返回值,即文件中最后一个命令的返回值。另外source允许在文件内的脚本中使用return命令终止过程。使用source命令,可以将一个大的脚本分为小的模块,又一个主脚本用source调用其他的脚本模块。可以通过把过程定义放到一个文件中,把可重用的过程建立成库。
1.7break和continue命令
break 命令可以用来无条件停止并跳出循环;continue 命令可以用来结束目前这一轮的循环,直接跳到下一轮循环。
2Perl
2.1布尔值判断
如果是数字,0表示假,其它所有数字都是真。
如果是字符串,空字符串('')为假,其它所有字符串为真(有例外,见下一条)。
如果是字符串'0',perl是当作数值0来处理的,所以这是唯一的非空但为假的字符串。
如果既不是数字,也不是字符串,那么先转换为数字或字符串再做判断(也就是"undef"表示假,其它所有引用表示真)。
"!"表示取反。
perl有个技巧,将两个"!"一起用,相当于"负负得正",所以原来是真的仍然是真的,原来是假的仍然是假的。但实际上,perl会将它们转换值"1"和"undef"。
2.2条件判断if和unless
它们都是条件判断语句,都支持else、elsif子句。
其中CONDITION可以是任意一个标量值。布尔值的判断很简单,方式和bash shell有点类似,但有点相反。unless和if判断方式相反,if的condition为真则执行后面的代码,否则执行else或者退出if结构。unless则是condition为假时才执行后面的代码,否则执行else或退出unless结构。所以,unless相当于if的else部分,或者相当于if (!condition)。一般来说,不会用到unless的else语句,因为它完全可以改编成if语句。之所以有时候会使用unless而不是if的否定形式,是因为有时候的条件语句用if来写确实不方便。
2.3三目运算符
perl也支持三目运算符:如果expression返回真,则整个表达式返回if_true,否则返回if_false。
例如,求平均值,如果$n=0,则输出"------"。
它等价于:
三目运算符可以写出更复杂的分支:
执行结果:
2.4逻辑运算符and(&&)、or(||)、not(!)
&&运算符只有两边为真时才返回真,且短路计算:expr1为假时直接返回false,不会评估expr2。||运算符只要一边为真时就返回真,且短路计算:expr1为真时直接返回true,不会评估expr2。and和or基本等价于对应的&&和||,但文字格式的逻辑运算符优先级非常低。not和!求反,同样文字格式的not的优先级很低。因为符号格式的逻辑运算符优先级很高,所以往往左边和右边都会加上括号,而文字格式的优先级很低,左右两边不需加括号。
or运算符往往用于连接两个"成功执行,否则就"的子句。例如,打开文件,如果打开失败,就结束该perl程序:
更常见的,还会分行缩进:
同样,and运算符也常用于连接两个行为:左边为真,就执行右边的操作(例如赋值)。
以下是3个语句是等价语句:
2.5while和until循环
until和其它某些语言的until循环有所不同,perl的until循环,内部的commands主体可能一次也不会执行,因为Perl会先进行条件判断,当条件为假时就执行,如果第一次判断就为真,则直接退出until。
2.6for循环
Perl中的for循环采取C语言的for风格,例如:
需要注意的是,上面的$i默认是全局变量,循环结束后还有效。可以使用my关键字将其声明为局部变量:
for循环不仅仅只支持数值递增、递减的循环方式,还支持其它类型的循环,只要能进行判断即可。见下面的例子。
for关键字后面括号中的3个表达式都可以省略,但两个分号不能省略:
如果省略第三个表达式,则表示一直判断,直到退出循环或者无限循环。如果省略第二个表达式,则表示不判断,所以会无限循环。如果省略第一个表达式,则表示不做初始赋值。例如,下面分别省略第三个表达式和省略所有表达式:
对于无限循环,Perl中更好更优化的方式是使用:
Perl中的for也支持成员测试性的遍历,就像shell中的for i in ...的操作一样,它期待一个列表上下文,表示遍历整个列表。如果省略控制变量,表示使用$_。例如:
像for遍历列表元素的操作,可以使用foreach来替代,大多数迭代列表的时候它们可以互换。
2.7foreach循环
foreach更适合用于遍历,所有foreach都能直接修改关键字为for而转换成for循环。当写成for格式的时候,perl通过判断括号中的分号来决定这是foreach循环还是for的普通循环。但for能实现的循环功能,foreach不一定能实现,因为for中有初始变量,有条件判断,而foreach则是简单版的for循环。
先解释下foreach的用法:
例如,迭代从1到10的列表:
其中$i称为控制变量,每迭代一次都会从迭代列表中取出一个元素赋值给控制变量。可以省略控制变量,这时将采用默认的变量$_:
foreach可以改写为for:
关于for循环和foreach循环,如果在遍历过程中修改了元素的值,它会直接修改原始值。换句话说,迭代时赋值给控制变量的元素的引用,而不是赋值元素再赋值给控制变量。
当foreach/for遍历结束后,控制变量将复原为foreach/for遍历前的值(例如未定义的是undef)。
2.8each
each用来遍历hash或数组,每次迭代的过程中,都获取hash的key和value,数组的index(数值,从0开始)和元素值。each放在列表上下文,会返回key/value或index/element,放在标量上下文则只返回key或index。
遍历hash:
输出结果:
遍历数组:
输出结果:
each放在标量上下文:
输出结果:
2.9执行一次的语句块
使用大括号包围一段语句,这些语句就属于这个语句块,这个语句块其实是一个循环块结构,只不过它只循环一次。语句块也有自己的范围,例如可以将变量定义为局部变量。
2.10循环控制last、next、redo、LABEL
last相当于其它语言里的break关键字,用于退出当前循环块(for/foreach/while/until/执行一次的语句块都属于循环块),注意是只退出当前层次的循环,不会退出外层循环next相当于其它语言里的continue关键字,用于跳入下一次迭代。同样只作用于当前层次的循环redo用于跳转到当前循环层次的顶端,所以本次迭代中曾执行过的语句可能会再次执行标签用于为循环块打上标记,以便那些循环块控制关键字(last/next/redo)可以指定操作的循环层次。
以下是打标签的示例(标签建议采用大写):
上面的标签循环中,首先读取一行输入,然后进入foreach遍历,因为split没有参数,所以使用默认参数$_,这个$_所属范围是while循环,split以空格作为分隔符分割这一行,同时foreach也没有控制变量,所以使用默认的控制变量$_,这个$_所属范围是foreach循环。当foreach的$_能匹配字符串"error"则直接退出while循环,而不仅仅是自己的foreach循环。这里if语句后采用的匹配目标是属于foreach的默认变量$_。
例如,这个perl程序读取a.txt文件,其中a.txt文件的内容如下:
执行这个perl程序:
可见,只输出了a.txt中第二行Error前的4个单词。
2.11附加循环continue
perl中还有一个continue关键字,它可以是一个函数,也可以跟一个代码块。
如果指定了BLOCK,continue可用于while和foreach之后,表示附加在循环结构上的代码块。
每次循环中都会执行此代码块,执行完后进入下一循环。
在continue代码块内部,也可以使用redo、last和next控制关键字。所以,这几个流程控制关键字更细致一点的作用是:redo、last直接控制循环主体,而next是控制continue代码块。所以:
实际上,while和foreach在没有给定continue的时候,逻辑上等价于给了一个空的代码块,这时next可以跳转到空代码而进入下一轮循环。
例如:
输出结果:
3Shell
和 Java、PHP 等语言不一样,sh 的流程控制不可为空,如(以下为 PHP 流程控制写法):
在 sh/bash 里可不能这么写,如果 else 分支没有语句执行,就不要写这个 else。
3.1if else
if 语句语法格式:
写成一行(适用于终端命令提示符):
末尾的 fi 就是 if 倒过来拼写。
3.2if else-if else
if else-if else 语法格式:
if else 的 [...] 判断语句中大于使用 -gt,小于使用 -lt。
如果使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <。
以下实例判断两个变量是否相等:
输出结果:
使用 ((...)) 作为判断语句:
输出结果:
if else 语句经常与 test 命令结合使用,如下所示:
输出结果:
3.3for循环
与其他编程语言类似,Shell支持for循环。
for循环一般格式为:
写成一行:
当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的 shell 命令和语句。in 列表可以包含替换、字符串和文件名。in列表是可选的,如果不用它,for循环使用命令行的位置参数。例如,顺序输出当前列表中的数字:
输出结果:
顺序输出字符串中的字符:
输出结果:
3.4while 语句
while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为:
以下是一个基本的 while 循环,测试条件是:如果 int 小于等于 5,那么条件返回真。int 从 1 开始,每次循环处理时,int 加 1。运行上述脚本,返回数字 1 到 5,然后终止。
运行脚本,输出:
while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按结束循环。
3.5无限循环
无限循环语法格式:
或者
或者
3.6until 循环
until 循环执行一系列命令直至条件为 true 时停止。until 循环与 while 循环在处理方式上刚好相反。一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。until 语法格式:
condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:
运行结果:
输出结果为:
3.7case ... esac
case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case ... esac 语法格式如下:
case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。下面的脚本提示输入 1 到 4,与每一种模式进行匹配:
输入不同的内容,会有不同的结果,例如:
下面的脚本匹配字符串:
输出结果为:
3.8跳出循环
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue。
break 命令允许跳出所有循环(终止执行后面的所有循环)。下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
执行以上代码,输出结果为:
continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。对上面的例子进行修改:
运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行
参考文献
https://www.runoob.com/linux/linux-shell-process-control.html
https://www.junmajinlong.com/shell/script_course/shell_flow_control/
https://blog.csdn.net/J080624/article/details/107457247
https://www.cnblogs.com/kane1990/archive/2011/12/19/2293978.html
https://blog.csdn.net/sinat_41774721/article/details/122928261
https://www.cnblogs.com/huguodong/p/5882597.html
https://www.cnblogs.com/f-ck-need-u/p/9511878.html
https://www.jc2182.com/perl/perl-workflow.html
https://blog.csdn.net/sunshoupo211/article/details/30747887
https://www.runoob.com/perl/perl-conditions.html
https://www.runoob.com/perl/perl-loops.html