shell 脚本的使用该熟练起来了,你说呢?(篇四)

用户头像
良知犹存
关注
发布于: 2020 年 12 月 14 日

继续前一篇的文章:



shell脚本的使用该熟练起来了,你说呢?(篇一)



shell脚本的使用该熟练起来了,你说呢?(篇二)



shell脚本的使用该熟练起来了,你说呢?(篇三)



文章里面测试的命令脚本文件,大家关注我公众号后,可以私信我领取文件。



作者:良知犹存



转载授权以及围观:欢迎添加微信公众号:羽林君






Shell test 命令



Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的试。



数值测试



参数

说明



实例



num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi



输出结果:



两个数相等!





代码中的 [] 执行基本的算数运算,如:



a=5b=6
result=$[a+b] # 注意等号两边不能有空格
echo "result 为:$result"





结果为:



result 为:11





字符串测试



参数

说明



实例



num1="run"
num2="run1"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi



输出结果:



两个字符串不相等!





文件测试



参数

说明



实例:



cd /usr
if test -e bin
then
echo '文件已存在!'
else
echo '文件不存在!'
fi





输出结果:





另外,Shell 还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为: ! 最高, -a 次之, -o 最低。



实例:



cd /usr
if test -e bin -o -e ros
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi



输出结果:





Shell 流程控制



和Java、PHP等语言不一样,sh的流程控制不可为空,如(以下为PHP流程控制写法):实例



<?php
if (isset($_GET["q"])) {
search(q);
}
else {
// 不做任何事情
}
?>





在sh/bash里可不能这么写,如果else分支没有语句执行,就不要写这个else。






**if **



if 语句语法格式:



if condition
then
command1
command2
...
commandN
fi





写成一行(适用于终端命令提示符):



if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi





末尾的fi就是if倒过来拼写,后面还会遇到类似的。



if else



if else 语法格式:



if condition
then
command1
command2
...
commandN
else
command
fi





if else-if else



if else-if else 语法格式:



if condition1
then
command1
elif condition2
then
command2
else
commandN
fi





以下实例判断两个变量是否相等:



实例



a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi





输出结果:





if else 语句经常与 test 命令结合使用,如下所示:



实例



num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo '两个数字相等!'
else
echo '两个数字不相等!'
fi





输出结果:








for 循环



与其他编程语言类似,Shell支持for循环。



for循环一般格式为:



for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done





写成一行:



for var in item1 item2 ... itemN; do command1; command2… done;





当变量值在列表里,for循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的shell命令和语句。in列表可以包含替换、字符串和文件名。in列表是可选的,如果不用它,for循环使用命令行的位置参数。



例如,顺序输出当前列表中的数字:



实例



for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done





输出结果:





顺序输出字符串中的字符:



for str in 'This is a string'
do
echo $str
done





输出结果:





shell 中的 for 循环不仅可以用文章所述的方法。



对于习惯其他语言 for 循环的朋友来说可能有点别扭。



for((assignment;condition:next));do
command_1;
command_2;
commond_..;
done;





如上所示,这里的 for 循环与 C 中的相似,但并不完全相同。



通常情况下 shell 变量调用需要加 $,但是 for 的 (()) 中不需要,下面来看一个例子:



for((i=1;i<=5;i++));do
echo "这是第 $i 次调用";
done;





执行结果:





与 C 中相似,赋值和下一步执行可以放到代码之前循环语句之中执行,这里要注意一点:如果要在循环体中进行 for 中的 next 操作,记得变量要加 $,不然程序会变成死循环。






while 语句



while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:



while condition
do
command
done





以下是一个基本的while循环,测试条件是:如果int小于等于5,那么条件返回真。int从0开始,每次循环处理时,int加1。运行上述脚本,返回数字1到5,然后终止。



实例



int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done





运行脚本,输出:





以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上

来表示变量,具体可查阅:let 命令。(let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。)



while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按结束循环。



实例



echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done





运行脚本,输出类似下面:





无限循环



无限循环语法格式:



while :
do
command
done





或者



while true
do
command
done





或者



for (( ; ; ))








until 循环



until 循环执行一系列命令直至条件为 true 时停止。



until 循环与 while 循环在处理方式上刚好相反。



一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。



until 语法格式:



until condition
do
command
done





condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。



以下实例我们使用 until 命令来输出 0 ~ 9 的数字:



实例



a=1
until [ ! $a -lt 6 ]
do
echo $a
a=`expr $a + 1`
done





运行结果:



输出结果为:








case



Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:



case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac





case工作方式如上所示。取值后面必须为单词in,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。



取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。



下面的脚本提示输入1到4,与每一种模式进行匹配:



实例



echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac





输入不同的内容,会有不同的结果,例如输入了3:








跳出循环



在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。



break命令



break命令允许跳出所有循环(终止执行后面的所有循环)。



下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。



实例



while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done





执行以上代码,输出结果为:





continue



continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。



对上面的例子进行修改:



实例



while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done





运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。






case ... esac



case ... esac 与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。



case ... esac 语法格式如下:



case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac





case 后为取值,值可以为变量或常数。



值后为关键字 in,接下来是匹配的各种模式,每一模式最后必须以右括号结束,模式支持正则表达式。



实例



site="baidu"

case "$site" in
"baidu") echo "百度搜索"
;;
"google") echo "Google 搜索"
;;
"taobao") echo "淘宝网"
;;
esac





输出结果为:





Shell 函数



linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。



shell中函数的定义格式如下:



[ function ] funname [()]
{
action;

[return int;]
}





说明:



  • 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。

  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255



下面的例子定义了一个函数并进行调用:



demoFun(){
echo "这是我的第一个 shell 函数!"}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"





输出结果:





下面定义一个带有return语句的函数:



funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"





输出类似下面:





函数返回值在调用该函数后通过 $? 来获得。



注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。






函数参数



在Shell中,调用函数时可以向其传递参数。在函数体内部,通过

n 的形式来获取参数的值,例如,1表示第一个参数,$2表示第二个参数...



带参数的函数示例:



funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73





输出结果:





注意,

10 不能获取第十个参数,获取第十个参数需要{10}。当n>=10时,需要使用${n}来获取参数。



另外,还有几个特殊字符用来处理参数:



参数处理

说明



?** 仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过 **? 获得。



测试代码:



function demoFun1(){
echo "这是我的第一个 shell 函数!"
return `expr 1 + 1`
}

demoFun1
echo $?

function demoFun2(){
echo "这是我的第二个 shell 函数!"
expr 1 + 1
}

demoFun2
echo $?
demoFun1
echo 在这里插入命令!
echo $?





输出结果:





调用 demoFun2 后,函数最后一条命令 expr 1 + 1 得到的返回值($?值)为 0,意思是这个命令没有出错。所有的命令的返回值仅表示其是否出错,而不会有其他有含义的结果。



第二次调用 demoFun1 后,没有立即查看

? 的值,而是先插入了一条别的 echo 命令,最后再查看 ? 的值得到的是 0,也就是上一条 echo 命令的结果,而 demoFun1 的返回值被覆盖了。



下面这个测试,连续使用两次 echo $?,得到的结果不同,更为直观:



function demoFun1(){
echo "这是我的第一个 shell 函数!"
return `expr 1 + 1`
}

demoFun1
echo $?
echo $?





输出结果:





函数与命令的执行结果可以作为条件语句使用。要注意的是,和 C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false。



请参见下例:



echo "Hello World !" | grep -e Hello
echo $?
echo "Hello World !" | grep -e Bye
echo $?
if echo "Hello World !" | grep -e Hello
then
echo true
else
echo false
fi

if echo "Hello World !" | grep -e Bye
then
echo true
else
echo false
fi

function demoFun1(){
return 0
}

function demoFun2(){
return 12
}

if demoFun1
then
echo true
else
echo false
fi

if demoFun2
then
echo ture
else
echo false
fi





其执行结果如下:





grep 是从给定字符串中寻找匹配内容的命令。首先看出如果找到了匹配的内容,会打印匹配部分且得到的返回值

? 为 0,如果找不到,则返回值 ? 为 1。



接下来分别将这两次执行的 grep 命令当作条件语句交给 if 判断,得出返回值

? 为 0,即执行成功时,条件语句为 true,当返回值 ? 为 1,即执行失败时,条件语句为 false。



之后再用函数的 return 值作为测试,其中 demoFun1 返回值为 0,demoFun2 返回值选择了任意一个和 0 不同的整数,这里为 12。



将函数作为条件语句交给 if 判断,得出返回值为 0 时,依然为 true,而返回值只要不是 0,条件语句都判断为 false。



Shell 输入/输出重定向



大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。



重定向命令列表如下:



命令

说明



需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。



输出重定向



重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:



command1 > file1





上面这个命令执行command1然后将输出的内容存入file1。



注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。



实例



执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):



$ who > users





执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。



你可以使用 cat 命令查看文件内容:



$ cat users
lyn tty7 2020-12-04 10:22 (:0)





输出重定向会覆盖文件内容,请看下面的例子:



$ echo "学习shell不迷路" > users
$ cat users
学习shell不迷路





如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:



$ echo "学习shell不迷路" >> users
$ cat users
学习shell不迷路
学习shell不迷路





输入重定向



和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:



command1 < file1





这样,本来需要从键盘获取输入的命令会转移到文件读取内容。



注意:输出重定向是大于号(>),输入重定向是小于号(<)。



实例



接着以上实例,我们需要统计 users 文件的行数,执行以下命令:



$ wc -l users
2 users





也可以将输入重定向到 users 文件:



$ wc -l < users
2





注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。



command1 < infile > outfile





同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。



重定向深入讲解



一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:



  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。

  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。

  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。



默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。



如果希望 stderr 重定向到 file,可以这样写:



$ command 2>file





如果希望 stderr 追加到 file 文件末尾,可以这样写:



$ command 2>>file





2 表示标准错误文件(stderr)。



如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:



$ command > file 2>&1

或者

$ command >> file 2>&1





如果希望对 stdin 和 stdout 都重定向,可以这样写:



$ command < file1 >file2





command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。






Here Document



Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。



它的基本的形式如下:



command << delimiter
document
delimiter





它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。



注意:



结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。



开始的delimiter前后的空格会被忽略掉。



** **



** **



实例



在命令行中通过 wc -l 命令计算 Here Document 的行数:



wc -l << EOF
> 欢迎来到
> shell教程
> lyn
> EOF
3 #输出结果为 3 行





我们也可以将 Here Document 用在脚本中,例如:



#!/bin/bash
cat << EOF
欢迎来到shell教程
EOF





执行以上脚本,输出结果:








/dev/null 文件



如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:



$ command > /dev/null





/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。



如果希望屏蔽 stdout 和 stderr,可以这样写:



$ command > /dev/null 2>&1





注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。



这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。



$ command > file 2>&1
$ command >> file 2>&1





这里的**&**没有固定的意思



放在**>后面的&,表示重定向的目标不是一个文件**,而是一个文件描述符,内置的文件描述符如下



1 => stdout
2 => stderr
0 => stdin





换言之 2>1 代表将stderr重定向到当前路径下文件名为1regular file中,而2>&1代表将stderr重定向到文件描述符1的文件(即**/dev/stdout**)中,这个文件就是stdoutfile system中的映射



而**&>file是一种特殊的用法,也可以写成>&file**,二者的意思完全相同,都等价于



>file 2>&1





此处**&>或者>&**视作整体,分开没有单独的含义






顺序问题:



find /etc -name .bashrc > list 2>&1
# 我想问为什么不能调下顺序,比如这样
find /etc -name .bashrc 2>&1 > list





这个是从左到右有顺序的



第一种



xxx > list 2>&1





先将要输出到stdout的内容重定向到文件,此时文件list就是这个程序的stdout,再将stderr重定向到stdout,也就是文件list



第二种



xxx 2>&1 > list





先将要输出到stderr的内容重定向到stdout,此时会产生一个stdout的拷贝,作为程序的stderr,而程序原本要输出到stdout的内容,依然是对接在stdout原身上的,因此第二步重定向stdout,对stdout的拷贝不产生任何影响



对于上面 '2>&1',举个例子,比如说:



$ find /etc -names "*.txt" >list 2>&1





从左往右执行,执行到 >list,此时的 stdout 为 list;而执行到 2>&1,表示 stderr 重定向到 stdout,这里也就是 list 文件。



因为 [ find /etc -names "*.txt" ] 这条命令是错误的( -names 应该是 -name)。



本来要输出到终端屏幕的错误信息:



find: unknown predicate `-names`





被重定向到了 stdout 也就是 list 文件中,所以屏幕不会出现错误信息,而是打印到了 list 文件中。



cat list 可以查看到 find: unknown predicate `-names' 就在里面。



直接在 FreeBSD 或者 csh 中使用 command > file 2>&1 时候会得到这个错误:Ambiguous output redirect



出错的原因在于 FreeBSD 默认使用 csh,在 csh 中如果想把标准输出和错误输出同时重定向到一个文件,需要用下面命令 command >& file



这就是我分享的shell脚本,其中参考了很多人的文章,如果大家有什么更好的思路,也欢迎分享交流哈。



**—*END*—

【1】 C++的智能指针你了解吗?



【2】嵌入式底层开发的软件框架简述

【3】CPU中的程序是怎么运行起来的 必读

【4】C++的匿名函数(lambda表达式)

【5】阶段性文章总结分析



本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。





更多分享,扫码关注我



参考链接:



https://www.runoob.com/linux/linux-shell-variable.html



发布于: 2020 年 12 月 14 日阅读数: 19
用户头像

良知犹存

关注

还未添加个人签名 2020.05.29 加入

还未添加个人简介

评论

发布
暂无评论
shell脚本的使用该熟练起来了,你说呢?(篇四)