最新要闻
- 天天快看:携程梁建章建议:取消中考 缩短中小学学制 提前2年上班
- 时代变了!日系豪华败走中国:停产停售、官网504、全面退场
- 【天天播资讯】《春晚》收视率史低 2023年很多节目真实生活取材 这次必看
- 全球报道:干掉致命疾病!全球首款蜜蜂疫苗获批
- 转子发动机回归!马自达官宣:增程式版MX-30本月发布
- 环球速读:解决安卓四大不可能 李杰:一加11 16GB流畅用四年
- 环球快资讯:再也不怕侧方停车!现代展示e-Corner系统:还能“坦克掉头”
- 百事通!成了!微信:视频号用户总使用时长接近朋友圈80%
- 天天要闻:开袋即食 一口弹牙 大牌优形肉肠0.99元/根(商超2.5元)
- 快资讯:期待!《生化危机4:重制版》威斯克或将登场
- 特斯拉门店:降价维权后销量翻倍!全国一天提车量超1万台
- 2023支付宝集五福最全攻略 神秘玩法千万别错过
- 琥珀银杏果是什么东西?琥珀银杏果是什么地方的菜?
- 兵长一米六是什么意思?兵长一米六顺口溜
- 美髯公指的是谁?美髯公是什么意思?
- 金坷垃是什么意思?金坷垃是什么时候的梗?
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
13种Shell逻辑与算术,能写出5种算你赢!
相较于最初的 Bourne shell,现代 bash 版本的最大改进之一体现在算术方面。早期的 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语句,如下:
let COUNT=COUNT+5
同$(())一样,在使用变量时不需要使用$符号。但是,当我们需要使用let进行COUNT=$((COUNT + 5 + MAX * 2))格式的运算时,需要使用到引号‘’,如下:
let COUNT+="5+MAX*2"
let 语句和 $(( )) 语法的另一处重要区别在于两者处理空白字符(空格字符)的方式不同。对 let 语句来说,要么添加引号,要么赋值运算符(=)和其他运算符两边不能出现空格。必须将运算符和操作数放在一起形成一个单词。以下两种写法都没问题。
let i=2+2let "i = 2 + 2"
$(( )) 语法就宽松多了,它允许各种空白字符出现在双括号内。这种写法不易出错,代码的可读性也要好得多,是我们执行 bash 整数运算时的首选方式。
3、bash中的赋值运算符
4、条件分支if
条件判断,逻辑分支是任何一个语言都会遇到的问题,bash中同其他语言类似,都是使用if进行条件判断,如下:
if [ $# -lt 3 ]then printf "%b" "Error. Not enough arguments.\n" printf "%b" "usage: myscript file1 op file2\n" exit 1fi
或者:
if (( $# < 3 ))then printf "%b" "Error. Not enough arguments.\n" printf "%b" "usage: myscript file1 op file2\n" exit 1fi
以下是一个带有 elif(bash 中的 else-if)和 else 子句的完整if 语句。如下:
if (( $# < 3 ))then printf "%b" "Error. Not enough arguments.\n" printf "%b" "usage: myscript file1 op file2\n" exit 1elif (( $# > 3 ))then printf "%b" "Error. Too many arguments.\n" printf "%b" "usage: myscript file1 op file2\n" exit 2else printf "%b" "Argument count correct. Proceeding...\n"fi
关于if,我们有两个问题需要明白,分别是:
- if 语句的基本结构
- if 表达式的不同语法(括号或方括号,运算符或选项)
5、if的基本结构
按照 bash 手册页中的描述,if 语句的一般形式如下所示。
if list; then list; [ elif list; then list; ] ... [ else list; ]fi
[ 和 ] 用于划分语句中的可选部分(例如,有些 if 语句中就没有else 子句)。我们先来看看不带任何可选部分的 if 语句。
最简单的 if 语句形式如下所示。
if list; then list; fi
在 bash 中,和换行符一样,分号的作用也是结束某个语句。我们可以用分号将解决方案部分中的示例塞进更少的行中,但使用换行符的可读性更好。then list的存在看起来是有意义的,其中的语句在 if 条件为真的情况下执行(我们也可以从其他编程语言中猜测出来)。但是,if list 算是怎么回事?难道不应该是 if expression 吗?
没错,但这是 shell,一个命令处理器。它的主要任务就是执行命令。因此,if 后面的 list就是放置命令列表的地方。你可能会问,决定分支走向(then 子句或 else 子句)的是什么呢?答案是list 中最后一个命令的返回值。
我们通过一个有点奇怪的示例来说明这一点。如下:
$ cat trythis.sh // 查看脚本内容,如下所示if ls; pwd; cd $1; then echo successelse echo failedfi// 执行脚本,传递一个参数$ bash ./trythis.sh /tmp
在这个奇怪的脚本中,shell 会在选择分支前执行 3 个命令(ls、pwd、cd),其中 cd 命令的参数是调用该脚本时所提供的第一个命令行参数。如果没有提供参数,那就只执行 cd,返回到主目录中。结果会是怎样?你可以自己试试。最终是显示“success”还是“failed”,取决于 cd 命令是否执行成功。在示例中,cd 是 if语句命令列表中的最后一个命令。如果 cd 执行失败,就转到 else子句;但如果执行成功,则选择 then 子句。
6、if中的 [] 和 (())
我们一起来看下面的例子:
if test $# -lt 3then echo try again.fi
前面讲到if后面是list是命令列表,虽然此处不是命令列表,但有没有从中看出起码类似于单个 shell 命令(内建命令 test 接受参数并比较参数值)的东西?
在本章开头,我们给出的第一个示例中开头的 if [ $# -lt 3 ] 看起来很像test 命令。这是因为 [ 其实只是相同命令的不同名称而已。(出于可读性和美观方面的考虑,调用 [ 时还要求将 ] 作为最后一个参数。)因此,对于该语法,if 语句中的表达式其实就是一个只包含单个命令(test 命令)的列表。
在早期的 Unix 中,test 是一个独立的可执行文件,[ 只是指向该文件的链接。现在两者仍以可执行文件的形式存在,但bash 也将它们实现为内建命令。
那么 if (( $# < 3 )) 又是什么意思?
双括号是复合命令的一种。因为它会对其中的算术表达式求值,所以能在 if 语句中派上用场。这是一处比较新的 bash 改进 ,专门用于有 if 语句的场合。
可用于 if 语句的这两种语法之间的重要区别在于测试的表达方式及其能够测试的对象种类。
双括号仅限于算术表达式,方括号还可以测试文件特性,但后者的算术测试语法远不如前者方便,尤其是用括号将表达式划分成若干子表达式时。
当我们使用 [ ] 时,一定要注意空格是必须存在的,如下图:
if [ -d "/opt/" ]
7、测试文件的特性
为了提高脚本的稳健性,你希望在读取输入文件前先检查该文件是否存在;另外,还想在写入输出文件前确认其是否具备写权限,在用cd 切换目录前看看到底有没有这个目录。这些该如何在 bash 脚本中实现呢?如下所示:
#!/usr/bin/env bash# 实例文件:checkfile#DIRPLACE=/tmpINFILE=/home/yucca/amazing.dataOUTFILE=/home/yucca/more.resultsif [ -d "$DIRPLACE" ] // 判断是否目录then cd $DIRPLACE if [ -e "$INFILE" ] // 判断文件是否存在 then if [ -w "$OUTFILE" ] // 判断文件是否拥有写权限 then doscience < "$INFILE" >> "$OUTFILE" else echo "cannot write to $OUTFILE" fi else echo "cannot read from $INFILE" fielseecho "cannot cd into $DIRPLACE"fi
将各种文件名引用全都放入了引号,以防路径名中包含空格。在上面的例子,我们使用了测试文件是否是目录(-d)、文件是否存在(-e)、文件是否有写权限(-w),我们也可以测试一些别的文件特性,其中有 3 个特性要用到双目运算符(接受两个文件名)。
- FILE1-nt FILE2是否更新(检查文件的修改时间)。现有文件要比不存在的文件“新”。
- FILE1-ot FILE2是否更旧。同样,不存在的文件要比现有文件“旧”。
- FILE1-ef FILE2具有相同设备和 inode 编号(即便由不同链接所指向,也视为相同的文件)
前面使用的-e、-d、-w都属于单目运算符,其形式为 option filename,例如,if [ -e myfile ]
8、测试多个特性
前面,我们测试每个特性都是使用单独一个if语句,那么我们测试多个特性时,必须嵌套if语句吗?
使用 -a(逻辑与)和 -o(逻辑或)运算符将多个测试条件组合成一个表达式。例如:
if [ -r $FILE -a -w $FILE ]
该 if 语句会测试指定文件是否可读并且可写。
测试时,为啥不加上-e呢?因为所有的文件测试条件都隐含了该文件存在的测试,所以测试文件可读性时不用测试文件是否存在。如果文件不存在,自然也就不可读。这些逻辑运算符(-a 表示 AND,-o 表示 OR)可用于所有的测试条件,并不局限于文件测试。
同一个语句中可以出现多个 AND/OR。你可能要用括号来获得正确的优先级,比如 a and (b or c),但一定要记得在括号前加上反斜杠或将括号放进引号,以消除其特殊含义。如下:
if [ -r "$FN" -a \( -f "$FN" -o -p "$FN" \) ]
9、测试字符串特性
你希望在使用字符串前先检查一下它们的值。这些字符串可以是用户输入、读入的文件或传入脚本的环境变量。如何用 bash 脚本实现呢?
你可以在 if 语句中使用单方括号形式的 test 命令进行一些简单的测试,其中包括检查变量是否包含文本以及两个变量中的字符串是否相同。如下脚本所示:
# 使用命令行参数VAR="$1"## if [ "$VAR" ]这种形式通常也管用,但并不是一种好的写法,加上-n会更清晰if [ -n "$VAR" ]thenecho has textelseecho zero lengthfiif [ -z "$VAR" ]thenecho zero lengthelse echo has textfi
长度为 0 的变量有两种:设置为空串的变量和不存在的变量。示例中的测试并不区分这两种情况。它只关心变量中是否有字符存在。
重要的是要将 $VAR 放进引号,否则测试会被一些怪异的用户输入干扰。如果 $VAR 的值是 x -a 7 -lt 5 且没有使用引号,那么下列语句:
if [ -z $VAR ]
就会变成(在变量扩展之后):
if [ -z x -a 7 -lt 5 ]
10、测试等量关系
你想要检查两个 shell 变量是否相等,但是存在两种测试运算符:-eq 和 =(或 ==)。该用哪个呢?
你需要的比较类型决定了该用哪种运算符。
- 如果是进行数值比较,可以使用 -eq 运算符。
- 如果是进行字符串比较,则使用 =(或 ==)运算符。
下面,我们通过一个简单的脚本例子来演示,如下:
## 老生常谈的字符串与数值比较#VAR1=" 05 "VAR2="5"printf "%s" "do they -eq as equal? "if [ "$VAR1" -eq "$VAR2" ]thenecho YESelseecho NOfiprintf "%s" "do they = as equal? "if [ "$VAR1" = "$VAR2" ]thenecho YESelseecho NOfi
如果,我们运行脚本,则会得到如下结果:
$ ./脚本名do they -eq as equal? YESdo they = as equal? NO$
尽管两个变量的数值相等(5),但从字符角度来看,前导字符 0 和空白字符意味着这两个字符串并不相同。
= 和 == 都可以使用,但 = 符合 POSIX 标准,可移植性更好。
使用if,我们可以在脚本中进行分支判断。但是对于系统而言,循环同分支一样是常见需求。所以Shell一样支持循环操作
11、循环一段时间
对于算术条件,使用 while 循环:
while (( COUNT < MAX )) // 判断条件是否成立do // 语法要求,以do开始 some stuff let COUNT++done // 语法要求,以done结束
对于文件系统相关的条件:
while [ -z "$LOCKFILE" ]do some thingsdone
第一个 while 语句中的双括号界定了算术表达式,这很像 shell 变量赋值中用到的 $(( ))。双括号内出现的变量名表示取值。也就是说,不需要写成 $VAR,直接在括号中使用 VAR就行了。
while [ -z"$LOCKFILE" ] 中的方括号和 if 语句中的一样,等同于使用 test 命令。
使用(( )) 时,shell 会对其中的表达式求值,如果结果为非0,那么 (( )) 就返回 0;如果结果为 0,则返回 1。这意味着我们可以像 Java 或 C 程序员那些书写表达式,但 while 语句沿用的仍旧是 bash 那一套,视 0 为真。实际上,这意味着我们可以编写一个无限循环:
while (( 1 ))do ...dosomethingdone
12、循环若干次
如果需要循环够一定次数。可以使用 while 循环,在计数时进行测试,不过编程语言中的 for 循环正是针对这种情况设计的。那么,如何在 bash 中实现呢?
使用 for 循环语法的一种特例,看起来和 C 语言中的差不多,但使用的是双括号。
for (( i=0 ; i < 10 ; i++ )) ; do echo $i ; done
在早期的 shell 版本中,for 循环只能按照固定的列表项进行迭代。和文件名之类的打交道时,shell 脚本是面向单词的,就此而言,这算得上是一个不错的创新。但如果需要计数,用户会发现自己可能写出了如下代码。
for i in 1 2 3 4 5 6 7 8 9 10do echo $idone
看起来还行,尤其是循环次数不多时。可是说实话,换成 500 次循环可就不好使了。
bash 2.04 版开始引入一种 for 循环的变体,语法与 C 语言类似。其一般形式如下所示。
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
双括号表明这是算术表达式,在其中引用变量时,不用加 $(但 $1等位置参数除外),只要是 bash 中出现双括号的地方,均是如此。该表达式是整数表达式,可以使用包括逗号(用于在一个表达式中放入多个操作)在内的大量运算符。
for (( i=0, j=0 ; i+j < 10 ; i++, j++ ))do echo $((i*j))done
for 循环先初始化了两个变量($i 和 $j),然后在第二个更复杂的子表达式中对 $i 和 $j 求和,接着判断是否小于 10。第三个子表达式再次用逗号运算符累加这两个变量。
13、在循环中使用浮点值
带有算术表达式的 for 循环只能执行整数运算。如果是浮点值,该怎么办呢?
如果系统提供了 seq 命令,则可以用它来生成浮点值。
for fp in $(seq 1.0 .01 1.1)do echo $fp; other stuff toodone
seq 命令会生成一系列浮点值,每行一个。该命令的参数依次是起始值、增量、结束值。$() 在子 shell 中执行命令,返回结果中的换行符会被空白字符替换,因此,就 for 循环而言,每个值都是字符串。
本文由
传智教育博学谷
教研团队发布。如果本文对您有帮助,欢迎
关注
和点赞
;如果您有任何建议也可留言评论
或私信
,您的支持是我坚持创作的动力。转载请注明出处!
13种Shell逻辑与算术,能写出5种算你赢!
提升代码可读性,减少if-else的几个小技巧
天天快看:携程梁建章建议:取消中考 缩短中小学学制 提前2年上班
时代变了!日系豪华败走中国:停产停售、官网504、全面退场
焦点速看:DOS初识
【天天播资讯】《春晚》收视率史低 2023年很多节目真实生活取材 这次必看
全球报道:干掉致命疾病!全球首款蜜蜂疫苗获批
转子发动机回归!马自达官宣:增程式版MX-30本月发布
环球速读:解决安卓四大不可能 李杰:一加11 16GB流畅用四年
环球快资讯:再也不怕侧方停车!现代展示e-Corner系统:还能“坦克掉头”
ElasticSearch必知必会-基础篇
环球快报:火山引擎 DataLeap 通过中国信通院测评,数据管理能力获官方认可!
全球速递!开源动物行为分析实验箱(斯金纳箱)需求调研分析
世界看点:低代码开发:释放数字化生产力
文件IO操作开发笔记(二):使用Cpp的ofstream对磁盘文件存储进行性能测试以及测试工具
百事通!成了!微信:视频号用户总使用时长接近朋友圈80%
天天要闻:开袋即食 一口弹牙 大牌优形肉肠0.99元/根(商超2.5元)
快资讯:期待!《生化危机4:重制版》威斯克或将登场
特斯拉门店:降价维权后销量翻倍!全国一天提车量超1万台
2023支付宝集五福最全攻略 神秘玩法千万别错过
琥珀银杏果是什么东西?琥珀银杏果是什么地方的菜?
兵长一米六是什么意思?兵长一米六顺口溜
美髯公指的是谁?美髯公是什么意思?
金坷垃是什么意思?金坷垃是什么时候的梗?
肥皂水是碱性还是酸性?肥皂水的主要化学成分是什么?
主角叫萧破天的小说有哪些?主角叫萧破天的全部小说
转世仁波切是什么意思?转世仁波切怎么认证?
电信拨打长途前面加什么?电信拨打长途要钱吗?
今日最新!金子塔图,自定义图表,伪漏斗图
环球今日报丨Codeforces 1704 F Colouring Game 题解 (结论,SG函数)
dnf武极觉醒技能怎么点?DNF武极觉醒技能介绍
倒角外倒角内倒角怎么区分?倒角外倒角内倒角的区别有哪些?
申怡走进云南白沙湾小学开讲公益语文课 用语文点亮希望的灯火
【新视野】专心打造小米汽车!雷军已卸任多家小米旗下公司职务
天天新资讯:《中国奇谭》爆火出圈!淘宝周边已卖断货 网友建议快出小野猪毛刷
世界速看:特斯拉二把手上位之路:朱晓彤如何打动马斯克?
环球资讯:比特斯拉多卖40万辆 比亚迪提前锁定2022年全球新能源销冠
【环球热闻】日志瘦身骚操作:从 5G 优化到 1G。
重磅发布丨从云原生到 Serverless,先行一步看见更大的技术想象力
世界简讯:rsync使用技巧
最新快讯!美国要给飞机安装5G信号屏蔽仪:避免影响航空安全
足坛再无“大圣” 33岁老将贝尔宣布退役
世界报道:2023年世界航天首次失利!英国维珍轨道火箭异常:9颗卫星没了
极狐考拉发布首款智能亲子车:有侧滑门、自带电子安全座椅
你集齐五福了吗?扫这张图能拿额外福卡 每天都有
【全球新要闻】劳斯莱斯2022年销量刷百年纪录!土豪都喜欢定制车
【天天聚看点】1.47英寸大屏、14天续航!Redmi手环2年货节优惠:仅159元
当前热门:腾讯股价翻倍 港股又香了 2023年首只爆款基金20亿一天卖完
全球观热点:联合国:地球臭氧层有望在40年内恢复 已步入正轨
环球即时看!国外小伙买世界最亮手电筒 网友:天已亮 该去上班了
天天微动态丨清华教授:互联网公司很难形成垄断了
支付宝“集五福”正式开启:已有2.7万人火速集齐 分5亿红包
天天微速讯:最新安卓手机用户偏好榜:12G内存、512G存储逐渐成主流
每日热文:洛谷P3956. [NOIP2017. PJ]棋盘
Spring IOC官方文档学习笔记(八)之容器扩展点
HTML超文本标记语言4
【环球快播报】茅台董事长丁雄军:买不到茅台还吃不起冰激凌吗?
小米12S Ultra官网缺货!卢伟冰推荐小米13 Pro:影像也非常强
环球快讯:过去10年 全国130多个县第一次通上铁路
环球热门:2.5亿年前 96%的物种突然消失!中国学者有了重大发现
精彩看点:还在用20世纪90年代的破系统!美国航空巨头8天赔掉50个亿
ThreadLocal底层原理
焦点热文:小米13 Pro把单反相机技术塞入手机里!雷军:效果惊艳
AMD三颗新U齐发!锐龙9 7900、锐龙7 7700、锐龙5 7600图赏
暴雪国服代理谈判被曝进入"二选一"阶段:手游版权开发分成是核心
报道:SpaceX猎鹰重型火箭再次发射美军卫星:能抗核打击
环球今热点:在QEMU-KVM环境下部署Oracle 19.16 RAC
全球要闻:HTML超文本标记语言3
苹果中国官网上线智能家居板块:一个LED灯泡卖168元 贵吗?
世界实时:《三体》电视剧过审上微博热搜 网友:只求别魔改!
环球今日报丨去意已决?巴菲特再度减持比亚迪
全球快消息!真爱粉!苏州加油为抢一加11首发跑到上海:对新品非常期待
世界观速讯丨C++引用【cherno课程学习】
【天天报资讯】「年终总结」写了 3 年文章的我,昨天第一次露脸直播。
环球今日报丨【前端调试】- 利用 Performance 和 Memory 分析内存
热点!操作系统 — 精髓与设计原理(第一章 计算机系统概述)
世界播报:学习笔记——Mybatis映射文件根标签与子标签
全球即时看!又一豪华巨头败走中国 官网关停!长安终于可以独享车标了
环球新消息丨广告又来?Win11将在文件管理器显示“推荐内容”
AMD锐龙7000新U不讲武德:编号更高 频率反而更低
车企"英文+数字"的新车命名规则被吐槽无趣 不如中文
外星人M18首发!京东方推出18英寸480Hz笔记本屏
全球热议:java不同版本jdk切换
每日焦点!读博初始心境-求讨论
前沿热点:抖音2023年春节红包来了:分4亿 最高可得万元奖金
环球快看点丨彻底终结充电焦虑?韩国SK On新技术:汽车电池18分钟充满
世界微资讯!电竞手机要消亡!卢伟冰:有了Redmi K60就不需要电竞手机
国内演员指责电视收费乱象:大几千买的电视看不了 打开全要收费
NAT类型检测方案
世界热议:学习笔记——Mybatis核心配置文件概述及根标签
VisualStudio 使用 FastTunnel 辅助搭建远程调试环境
世界观焦点:“外星科技”加持!Redmi 12C支持双SIM卡+TF卡拓展:699元起
搜狐张朝阳谈工作意义:年轻人赚钱享乐的想法不对
同步小米13 Ultra!小米平板6/6 Pro来了:处理器、屏幕大升级
今晚20点整:京东年货红包重磅加码 最高9999元
360Hz超高刷也白菜价了 泰坦军团1ms电竞显示器到手1629元
火山引擎 DataTester:5 个优化思路,构建高性能 A/B 实验平台
环球新资讯:对话阿里云叔同:如何看待 2022 年云原生的发展,2023 年有哪些值得关注的技术?
全球新动态:字符串匹配算法综述
新资讯:网易云音乐用户画像资产治理及业务赋能