紧接上文

expr命令

expr命令用于求表达式的值,格式为:

expr 表达式

表达式说明:

  1. 用空格隔开每一项
  2. 用反斜杠放在shell特定的字符前面(发现表达式运行错误时,可以试试转义)
  3. 对包含空格和其他特殊字符的字符串要用引号括起来
  4. expr会在stdout中输出结果。
    • 如果为逻辑关系表达式,则结果为真,stdout为1,否则为0。
  5. expr的exit code:00
    • 如果为逻辑关系表达式,则结果为真,exit code0,否则为1

字符串表达式

  1. length STRING
    返回STRING的长度
  2. index STRING CHARSET
    CHARSET中任意单个字符在STRING中最前面的字符位置,下标从==11==开始。如果在STRING中完全不存在CHARSET中的字符,则返回00
  3. substr STRING POSITION LENGTH
    返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。如果POSITIONLENGTH为负数,0或非数值,则返回空字符串。下标从==11==开始
1
2
3
4
5
str="Hello World!"

echo `expr length "$str"` # ``不是单引号,表示执行该命令,输出12
echo `expr index "$str" aWd` # 输出7,下标从1开始 W是第一个出现的字符
echo `expr substr "$str" 2 3` # 输出 ell

整数表达式

expr支持普通的算术操作,算术表达式优先级低于字符串表达式,高于逻辑关系表达式。

  1. + +\ - 加减运算。两端参数会转换为整数,如果转换失败则报错。

  2.  / %*\ /\ \% 乘,除,取模运算。两端参数会转换为整数,如果转换失败则报错。

  3. $() $可以改变优先级,但需要用反斜杠转义

1
2
3
4
5
6
7
8
9
10
a=3
b=4

echo `expr $a + $b` # 输出7
echo `expr $a - $b` # 输出-1
echo `expr $a \* $b` # 输出12,*需要转义
echo `expr $a / $b` # 输出0,整除
echo `expr $a % $b` # 输出3
echo `expr \( $a + 1 \) \* \( $b + 1 \)`
# 输出20,值为(a + 1) * (b + 1)

可以先正常写表达式最后在补上空格还有反斜杠

逻辑关系表达式

空或00为假的,其他为真,遵循短路原则,一旦确定则返回前面的值

  1. |
    如果第一个参数非空且非00,则返回第一个参数的值,否则返回第二个参数的值,但要求第二个参数的值也是非空或非00,否则返回00。如果第一个参数是非空或非00时,不会计算第二个参数。
  2. &\&
    如果两个参数都非空且非0,则返回第一个参数,否则返回0。如果第一个参为0或为空,则不会计算第二个参数。
  3. < <= = == != >= >
    比较两端的参数,如果为true,则返回1,否则返回0。”==””=”的同义词。”expr”首先尝试将两端参数转换为整数,并做算术比较,如果转换失败,则按字符集排序规则做字符比较。
  4. ()() 可以改变优先级,但需要用反斜杠转义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a=3
b=4

echo `expr $a \> $b` # 输出0,>需要转义
echo `expr $a '<' $b` # 输出1,也可以将特殊字符用引号引起来
echo `expr $a '>=' $b` # 输出0
echo `expr $a \<\= $b` # 输出1

c=0
d=5

echo `expr $c \& $d` # 输出0
echo `expr $a \& $b` # 输出3
echo `expr $c \| $d` # 输出5
echo `expr $a \| $b` # 输出3

test命令与判断符号[]

逻辑运算符&&和||

  1. && 表示与,|| 表示或

  2. 二者具有短路原则:

    • expr1 && expr2:当expr1为假时,直接忽略expr2

    • expr1 || expr2:当expr1为真时,直接忽略expr2

  3. 表达式的exit code为0,表示真;为非零,表示假。(与C/C++中的定义相反)

    00表示程序正常返回了 非0表示程序非正常返回了

test命令

在命令行中输入man test,可以查看test命令的用法。

  1. test命令用于判断文件类型,以及对变量做比较。

  2. test命令用exit code返回结果,而不是使用stdout。0表示真,非0表示假。

stdout就是程序运行中打印的内容(也就是echo的内容)

`command`或者$(command)会将命令中echo的内容截取下来变成一个字符串后返回

例如:

1
2
3
4
5
6
7
8
test 2 -lt 3         # 为真,返回值为0
echo $? # 输出上个命令的返回值,输出0
ls # 列出当前目录下的所有文件
homework output.txt test.sh tmp # ls结果
test -e test.sh && echo "exist" || echo "Not exist" # 判断文件是否存在
exist # test.sh 文件存在
test -e test2.sh && echo "exist" || echo "Not exist"
Not exist # testh2.sh 文件不存在

文件类型判断

命令格式

命令格式:test -e filename # 判断文件是否存在

测试参数 代表意义
-e 文件是否存在
-f 是否为文件
-d 是否为目录

文件权限判断

命令格式:test -r filename # 判断文件是否可读

测试参数 代表意义
-r 文件是否可读
-w 文件是否可写
-x 文件是否可执行
-s 是否为非空文件

整数间的比较

命令格式:test $a -eq $b # a是否等于b

测试参数 代表意义
-eq a是否等于b
-ne a是否不等于b
-gt a是否大于b
-lt a是否小于b
-ge a是否大于等于b
-le a是否小于等于b

-eq :equal (相等) -ne :not equal (不等)

-gt :greater than (大于) -ge :greater than or equal (大于或等于)

-lt :less than (小于) -le :less than or equal (小于或等于)

字符串比较

测试参数 代表意义
test -z STRING 判断STRING是否为空,如果为空,则返回0(exit code)
test -n STRING 判断STRING是否非空,如果非空,则返回true(-n可以省略)
test str1 == str2 判断str1是否等于str2
test str1 != str2 判断str1是否不等于str2

多重条件判定

命令格式:test -r filename -a -x filename

测试参数 代表意义
-a 两条件是否同时成立
-o 两条件是否至少一个成立
-! 取反。如 test ! -x file,当file不可执行时,返回true

判断符号[]

[]test用法几乎一模一样,更常用于if语句中。另外[[]][]的加强版,支持的特性更多。

1
2
[ 2 -lt 3 ]     # 为真,返回值为0
echo $? # 输出上个命令的返回值,输出0
1
2
3
4
5
6
ls                                 # 列出当前目录下的所有文件
homework output.txt test.sh tmp
[ -e test.sh ] && echo "exist" || echo "Not exist"
exist # test.sh 文件存在
[ -e test2.sh ] && echo "exist" || echo "Not exist"
Not exist # testh2.sh 文件不存在

注意:

  1. []内的每一项都要用空格隔开
  2. 中括号内的变量,最好用双引号括起来
  3. 中括号内的常数,最好用单或双引号括起来

例如:

1
2
3
4
name="acwing brown"
[ $name == "acwing brown" ]
# 错误,等价于 [ acwing brown == "acwing brown" ],参数太多
[ "$name" == "acwing brown" ] # 正确

== !=可以直接使用 应该是当成字符串比较

> < 需要加一个\

>= <= 好像不能使用

判断语句

if…then形式,类似于C/C++中的if-else语句。

单层if

命令格式:

1
2
3
4
5
6
if condition
then
语句1
语句2
...
fi

示例

1
2
3
4
5
6
7
8
a=3
b=4

if [ "$a" -lt "$b" ] && [ "$a" -gt 2 ]
then
echo ${a}在范围内
fi

结果: 3在范围内

单层if-else

命令格式

1
2
3
4
5
6
7
8
9
10
if condition
then
语句1
语句2
...
else
语句1
语句2
...
fi

示例

1
2
3
4
5
6
7
8
9
a=3
b=4

if ! [ "$a" -lt "$b" ]
then
echo ${a}不小于${b}
else
echo ${a}小于${b}
fi

结果: 3小于4

多层if-elif-elif-else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if condition
then
语句1
语句2
...
elif condition
then
语句1
语句2
...
else
语句1
语句2
...

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a=4

if [ $a -eq 1 ]
then
echo ${a}等于1
elif [ $a -eq 2 ]
then
echo ${a}等于2
elif [ $a -eq 3 ]
then
echo ${a}等于3
else
echo 其他
fi

结果: 其他

case…esac形式

类似于C/C++中的switch语句。

命令格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case $变量名称 in
值1)
语句1
语句2
...
;; # 类似于C/C++中的break
值2)
语句1
语句2
...
;;
*) # 类似于C/C++中的default
语句1
语句2
...
;;
esac

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a=4

case $a in
1)
echo ${a}等于1
;;
2)
echo ${a}等于2
;;
3)
echo ${a}等于3
;;
*)
echo 其他
;;
esac

结果:其他

循环语句

for…in…do…done

格式

1
2
3
4
5
6
for var in val1 val2 val3
do
语句1
语句2
...
done

示例1,输出a 2 cc,每个元素一行

1
2
3
4
for i in a 2 cc
do
echo $i
done

示例2,输出当前路径下的所有文件名,每个文件名一行:

1
2
3
4
for file in `ls`
do
echo $file
done

示例3,输出1-10

1
2
3
4
for i in $(seq 1 10)
do
echo $i
done

示例4,使用{1..10} 或者{a..z} 也支持倒序

1
2
3
4
for i in {a..z}
do
echo $i
done

for ((…;…;…)) do…done

注意是两个括号

命令格式

1
2
3
4
5
for ((expression;condition;expression))
do
语句1
语句2
done

示例,输出1-10,每个数占一行:

1
2
3
4
for ((i=1;i<=10;i++))
do
echo $i
done

while…do…done循环

命令格式:

示例

1
2
3
4
5
6
while condition
do
语句1
语句2
...
done

示例,文件结束符为Ctrl+d,输入文件结束符后read指令返回false

1
2
3
4
while read name
do
echo $name
done

until…do…done循环

当条件为真时结束。

这个和上面那个判断恰好相反 上面是判断是真就运行 这个是判断是假就结束

不要把他当成c++的do..while

命令格式:

1
2
3
4
5
6
until condition
do
语句1
语句2
...
done

示例,当用户输入yes或者YES时结束,否则一直等待读入。

1
2
3
4
until [ "${word}" == "yes" ] || [ "${word}" == "YES" ]
do
read -p "Please input yes/YES to stop this program: " word
done

break命令

跳出当前一层循环,注意与C/C++不同的是:break不能跳出case语句。他只能跳出循环

shell中的;;c/c++中的breakcase语句中类似的

如果在case语句中添加break且外层没用循环就会提醒

break: only meaningful in a ˋforˋ, ˋwhileˋ,or ˋuntilˋ loop

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
while read num
do
case $num in
0)
break
echo "看看会不会打印"
;;
*)
for i in $(seq 1 20)
do
if [ $i == 11 ]
do
break
done
echo -e "$i \c"
done
echo ""
;;
esac
echo "case 结束"
done
echo "while结束"

结果:只要输入的不是0 就打印1~10 case结束 如果输入的是0则打印 while结束

continue命令

跳出当前循环。

示例:

1
2
3
4
5
6
7
8
for ((i=1;i<=10;i++))
do
if [ `expr $i % 2` -eq 0 ]
then
continue
fi
echo $i
done

程序输出1-10中的所有奇数。

死循环的处理方式

如果可以打开该程序,则输入Ctrl+c即可。

否则可以直接关闭进程:

使用top命令找到进程的PID
输入kill -9 PID即可关掉此进程

ps aux返回当前打开的所有进程

函数

bash中的函数类似于C/C++中的函数,但return的返回值与C/C++不同,返回的是exit code,取值为0-2550表示正常结束。 没有return默认返回的是0

如果想获取函数的输出结果,可以通过echo输出到stdout中,然后通过$(function_name)来获取stdout中的结果。

函数的return值可以通过$?来获取。

函数中最后stdout每条echo中间用一个空格隔开,无论是否换行是否有\n 通通一个空格

命令格式:

1
2
3
4
5
[function] func_name() {  # function关键字可以省略
语句1
语句2
...
}

不获取return值和stdout

1
2
3
4
5
func() {
name=brown
echo "Hello $name"
}
func

结果 Hello brown

获取return值和stdout

不写return时,默认return 0

1
2
3
4
5
6
7
8
9
10
func() {
name=brown
echo "Hello $name"
return 123
}

output=$(func)
ret=$?
echo "output = $output"
echo "return = $ret"

结果:

1
2
output = Hello yxc
return = 123

函数的输入参数

在函数内,$1表示第一个输入参数,$2表示第二个输入参数,依此类推。

注意:函数内的$0仍然是文件名,而不是函数名。

1
2
3
4
5
6
7
8
9
10
11
12
sum() {
echo -e "\$0=$0 \n"
s=0
for i in $(seq 1 $#)
do
s=`eval expr '$s' + '$'"${i}"`
done
echo $s
return 0
}

echo $(sum 44 33 22 11)

eval暂时理解为解决嵌套

结果:$0=./test.sh 110

函数内的局部变量

可以在函数内定义局部变量,作用范围仅在当前函数内。

可以在递归函数中定义局部变量。

格式 local 变量名=变量值

1
2
3
4
5
6
7
8
9
#! /bin/bash

func() {
local name=brown
echo $name
}
func

echo $name

结果

1
2
brown

第一行为函数内的name变量,第二行为函数外调用name变量,会发现此时该变量不存在。