# 数学运算

在脚本中进行数学运算有很多种方式,你可以从下面选几种喜欢的

  • exper 命令,支持基本的(逻辑、比较)运算符,对于有歧义的符号 (如 * ),需要用 \*传送门
# expr 表达式
expr length “this is a test”	#计算字串长度
expr 14 % 9		#整数运算
expr 2 \* 10	#注意。参数和符号之间必须要有空格
  • $[表达式] ,更加方便,符号不会出现歧义,但是只能整数运算。
var1=$[1+5]
  • bc 计算器,实际上是一种编程语言, -q 可以不显示 bash 计算器冗长的欢迎信息。浮点运算是由内建变量 scale 控制的,必须将这个值设置为计算结果希望保留的小数位数 scale 默认是 0。
cyan@cyan-virtual-machine:~$ bc
# 欢迎信息
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1+3
4
319+42
361
cyan@cyan-virtual-machine:~$ bc -q
21/3
7
#内建变量不能在外面修改
cyan@cyan-virtual-machine:~$ echo $scale
cyan@cyan-virtual-machine:~$ bc -q
scale=3 #进入bc修改内建变量
21/3
7.000
# 还有一种修改内建变量的方法,常用于脚本中,格式为echo "options;expression" | bc
cyan@cyan-virtual-machine:~$ echo '21/4' | bc
5
cyan@cyan-virtual-machine:~$ echo 'scale=4;21/4' | bc
5.2500

可见,通过管道, bc 计算器还是挺好用的。再看一个输入重定向的例子

cyan@cyan-virtual-machine:~$ cat math.dat
scale=4
var1=1
var2=29
var3=5
var1/var3+var2
cyan@cyan-virtual-machine:~$ result=$(bc < math.dat)
cyan@cyan-virtual-machine:~$ echo $result
29.2000

有一点你可以看到,我们在 bc 中使用变量没有加 $ 符号。最后,你也可以试试内联输入重定向,我想,应该没什么问题吧?

# 结构化语句

该部分我就直接给格式了,你记住了就会使用。

# 选择语句

我们如何对 if 中的条件进行判断?

if condition1      #(退出状态码是 0)   
then
    commands_1
elif condition2
then
    commands_2
else
    commands_3
fi

condition 可以是一个命令的正确执行,也可以是字符串比较,数值比较,文件比较。

判断 condition 的命令为 test condtion 或者 [ condition ](必须有空格) 。同样的,如果有多个 condition 可以使用 && , ||

举个例子

cyan@cyan-virtual-machine:~/Templates$ cat tmp.txt
var=100
if [ $var -ge 10 ] && [ $var -le 190 ]
then
    echo '执行if语句块'
else
    echo '执行else语句块'
fi
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt
执行if语句块

再次声明,使用 [] 要加空格

# 比较方式

数值比较n1 -eq n2

符号选项含义
-eqequals(==)
-gtgreater than(>)
-gegreater equals(>=)
-ltless than(<)
-leless equals(<=)
-nenot equals(!=)

if [ $val -gt 5] ,注意,bash shell 只能使用整数。

字符串比较

直接使用 =,!=,\>,\< (大于小于,必须使用转义,否则就会被认为是重定向)。 在比较测试中,大写字母 < 小写字母,如 a>z>A 。而 sort 命令恰好相反, sort 使用的是系统本地化语言设置中定义的排序顺序。对于英语,本地化设置指定在排序顺序中,小写字母出现在大写字母前

-n-z 可以检查一个变量是否含有数据 (未被定义的变量默认长度为 0)

if[ -n $val ]	#如果 val 长度不为 0,就执行 then
if[ -z $val ]	#如果 val 长度为 0,就执行 then,z--zero

文件比较

符号选项含义
-d file检查 file 是否存在并是一个目录 (directory)
-e file检查 file 是否存在 (exits)
-f file检查 file 是否存在并是否是一个文件
-r file检查 file 是否存在并是否可读
-s file检查 file 是否存在并是否非空
-w file检查 file 是否存在并是否可写
-x file检查 file 是否存在并是否可执行
-G file检查 file 是否存在并且默认组与当前用户相同
-O file检查 file 是否存在并是否并属当前用户所有
file1 -nt file2检查 file1 是否比 file2 新
file1 -ot file2检查 file1 是否比 file2 旧

# case 语句

case variable in                            
pattern1 | pattern2)    commands1;;        
pattern3)   commands2;;                         
*) defalut commands3;;                      
esac                                       
# 例子
case $USER in
Bob | Mike)
	echo "Number one";;
Cyan)
     echo "Cyan";;
*)
	 echo "Have not found";;
esac

# 循环语句

# for 循环

for var in list         
do
    commands                
done                    
#例子
for test in Alabama Alaska Arizona Arkansas California
do
	echo The state is $test
done
  • $test 变量在剩余的 shell 脚本一直有效并且保持着最后一个变量的值 (当然是可以修改的)

  • list 中单个元素出现了单引号,空格等,要么在这个单独变量外部直接再使用一对引号,要么直接使用 \ 转义字符

  • list 的获取,也可以用命令的输出 + 命令替换for var in $(cat file) ,甚至是 /home/cyan/test/* 路径。当然,如果变量是文件名,要考虑文件名可能出现空格,那么在使用时就要使用引号: "$file"

# 模拟 ls 指令,打印指定目录下的所有文件(当然这里目录固定为 home/cyan)
# 如果想像 ls 路径那样传参数,需要用到之后的知识
cyan@cyan-virtual-machine:~/Templates$ cat tmp.txt
#/bin/bash
for file_name in /home/cyan/*
do
        echo $file_name
done
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt
/home/cyan/Desktop
/home/cyan/Documents
/home/cyan/Downloads
/home/cyan/Files
/home/cyan/Music
/home/cyan/OSIntroduction
/home/cyan/Pictures
/home/cyan/Public
/home/cyan/shtool-2.0.8
/home/cyan/shtool-2.0.8.tar.gz
/home/cyan/snap
/home/cyan/Templates
/home/cyan/Videos
/home/cyan/vmware-tools-distrib
  • list 中,各个元素之间的分隔符是由 IFS -- 内部字段分隔符决定的,一般会将空格,制表符,换行符作为默认字段分隔符如果想要更改分隔符,就必须在 shell 脚本 for 循环前面更改 IFS
cyan@cyan-virtual-machine:~/Templates$ cat data
jack bob cyan mike
mary smith
cyan@cyan-virtual-machine:~/Templates$ cat tmp.txt
#/bin/bash
for name in `cat data`
do
        echo $name
done
#得到的结果
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt
jack
bob
cyan
mike
mary
smith

这是因为 IFS 默认的是空格,制表符,换行符。在遇到第一个空格时,就已经分隔了。如果想要只以换行符为分隔符

cyan@cyan-virtual-machine:~/Templates$ cat tmp.txt
#/bin/bash
oldIFS=$IFS
IFS=$'\n'
for file_name in `cat data`
do
        echo $file_name
done
IFS=$oldIFS
cyan@cyan-virtual-machine:~/Templates$ ./tmp.txt
jack bob cyan mike
mary smith

C 语言风格的 for 循环

for (( i=1;i <= 20;i++ )) # i=$(expr $i+1)  i=$[$i + 1]   i=$(echo "scale=0;$i + 1" | bc)
do
    commands
done

# while 循环

while test conitions
do
    commands
done
# test 的替代物 [] 需要两边空格,而 expr 的替代物 $[] 不需要两边空格
# while 命令允许执行多个测试命令,但是只有最后一个测试命令的退出状态码会被用于决定什么时候结束循环
#   比如:while [$var1 -ge 0]
#               echo $var1
#   就是一个死循环
#注意,每个测试命令单独一行

# until 命令

until test commands
do
    commands
done
# until 语句,只有测试条件的退出码非 0,才会执行循环中列出的命令。