这个问题对于c/c++来说,那至少要有三五章来说明。对于shell来说那太简单了,严格来说,shell并没有数据类型的概念,无论你输入的是字符串还是数字,都是按照字符串类型来存储的。那么我们来看看shell是怎么用字符串实现和其它编程语言一样的功能的。
a
是一个字符,ab
是字符串。也可以认为a
是只有一个字符的字符串。'a'
、"a"
、a
是一样的。当然有时是必须有引号的,比如比较运算时。$
和反引号“`”符在单引号内不被当做运算符,只是字符sal@sal-laptop:/etc$ arr=(a b c d e) # 定义了arr数组,并且赋值 以空格分割圆括号内元素
sal@sal-laptop:/etc$ echo ${arr[@]} # 显示所有元素 是花括号
a b c d e
sal@sal-laptop:/etc$ echo ${!arr[@]} # 显示下标,下标就是指导第几个,从0开始
0 1 2 3 4
sal@sal-laptop:/etc$ echo ${#arr[@]} # 统计有多少个元素
5
sal@sal-laptop:/etc$ echo ${arr[0]} # 显示第一个,从0开始
a
sal@sal-laptop:/etc$ echo ${arr[3]} # 显示第三个,从0开始计算的!
d
sal@sal-laptop:/etc$ echo ${arr[5]} # 显示第五个,从0开始的,所以不存在第5个元素
sal@sal-laptop:/etc$ arr[5]=6 # 添加一个元素,第5个,值是6
sal@sal-laptop:/etc$ echo ${arr[5]} # 显示第五个,这会有了
6
sal@sal-laptop:/etc$ unset arr[5] # 删除第5个,就是上面添加的6
sal@sal-laptop:/etc$ echo ${arr[*]} # 再显示就没有第5个了
a b c d e
sal@sal-laptop:/etc$ unset arr[@] # 删除数组
sal@sal-laptop:/etc$ echo ${arr[*]} # 已经删除了
sal@sal-laptop:/etc$
shell的数组只能是一维的,说人话就是不能在数组中再套数组,它不能套娃!假设:arr1、arr2是数组,那么arr=(arr1 arr2)
是不行的。既然只能是一维的,就可以将数组视作以特殊字符(IFS,默认是空格)来分隔的字符串:
# 以下是在ubuntu20.04中运行的结果
test=('this is' 'a test')
# 大部分语言应该可以像这样写,但shell这样写结果明显不合理
for i in $test; do echo $i; done
this
is
# 换成标准写法,这好像也不太对吧?
for i in ${test[*]}; do echo $i; done
this
is
a
test
# 再来一个,还是不对,更不对了!
for i in "${test[*]}"; do echo $i; done
this is a test
# 换成@来试试,也不对
for i in ${test[@]}; do echo $i; done
this
is
a
test
# 再换个能得到正确结果的写法,这里不仅是* 和 @ 的区别,还有数组外的双引号
for i in "${test[@]}"; do echo $i; done
this is
a test
从上面的例子看出,shell实现数组的功能,就是在折腾字符串。
讲到数组,其实字符串也可以看作是不以空格分割的数据组合嘛,所以对于字符串就有了一套类似数组的方法,可以用来切割字符串:
echo ${s:3}
中3表示从第三个开始到结束。
echo ${s:3:4}
中3表示从第三个开始,后面的4表示取四个,超过可取数量就取所有。
可以用echo ${#s}
获取字符串的字符个数。其它删除、替换等方法不常用,这些字符串操作是可以在sh中使用的。
估计又有聪明人在嘀咕了:作者你忽悠,可劲的忽悠!前一章你还在给我说计算1加到100,还整了个看不懂的程序计算裴波那契数列的第37个数!才回个头的功夫,你又来说shell没有整数!那你来告诉我,前面那是在算什么?
所以shell对于整数的计算要写一大堆乱七八糟看不懂的符号呢,shell是用特殊的写法来表示这是整数计算的。常用的有c=$((1+2))
、let c=1+2
, 这两种最常用,方括号写法sh不支持。看到了吧,要这么折腾才能算出整数来。看清楚是整数哦,别折腾小数,shell不会算,要报错!要用其它方法计算,如果是c=$((10/3))
虽然能算,但结果变量c的值是3哦~
shell中的数学运算符:
+
:对两个整数做加法。-
:对两个整数做减法。*
:对两个整数做乘法。/
:对两个整数做除法。**
:对两个整数做幂运算。就是几次方,2**3=8%
:取模运算,第一个整数除以第二个整数求余数。聪明的读者突然觉得shell又能算结果有小数的除法了,先10/3得到3,再10%3得出1,这不就是小学的10/3=3…1+=
:加等于,在自身基础上加第二个整数。数列例子中i=$i+1
可以写作let $((i+=1))
。同样存在另几个运算的这种写法-=
、*=
、/=
、%=
,这种写法在shell中不常用。-eq
: 等于(equal)比较运算要写在方括号中[ 变量 -eq 值 ]
,方括号两边要有空格,下同-ne
:不等于(not equal)-gt
: 大于 (greate)-lt
: 小于(little)-ge
:大于等于(greate or equal)-le
:小于等于(little or equal)那我就是要计算小数怎么办?办法肯定有的,正经Linux中有个计算工具叫bc
,可以调用bc
来计算,这样写echo 1+2 | bc
。我们路由器上的Linux是没有的,但也是能算的:
echo 2 | awk '{print $1**3}' # $1在这里表示传入awk的第一个参数,以下类似
8
echo 2 3 | awk '{print $1**$2}'
8
echo 10 3 | awk '{print $1/$2}'
3.33333
echo 10 3.8 | awk '{print $1+$2}'
13.8
awk
很强大的!小数计算在编程中是很少用到的,不用太在意这点。早期的超级计算机经常说:浮点运算每秒多少多少亿次,这个浮点运算就是指带小数的数据计算。现代计算机算除法其实很快了,几个时钟周期就计算出来了,所以现代超算都说每秒运算多少亿亿次了~
shell这个语言设计时就根本没考虑做数学计算的问题,所以不用奇怪shell不能做数学。javascript也一样,甚至js算0.2加0.7等于0.89999999999999。
连超算都不以浮点运算来衡量性能了,更不能用小数计算来衡量编程语言的好坏。shell是Linux自带的语言,生来就是用来控制Linux系统的,是做运维工作第一选择。python语言虽然大多Linux也自带,做运维工作也很好,但很多时候python是用os模块调用shell的,你一样要明白shell语句怎么写,它才能工作。
除了整数运算,编程中对字符串的操作其实要多得多。也有比较运算符:
==
等于,判断两边的字符是否相同,也可以用在数值比较上,其实也可以写成=
,但为了和赋值操作符区别开来,不建议使用。写法[ “ab” == “AB” ],相等返回true,不相等返回false!=
不等于,判断两边的字符是否不相同,逻辑与上面相反-z
判断是否为空,空字符串就是没有一个字符写成这样:""
。用法:[ -z "$str" ]
,这个工作吧用==
也能干[ "$str" == "" ]
,str是空的就返回true。-n
判断是否不为空,逻辑与上面相反都说了shell是用于操作Linux系统的,所以它还有一套用于文件的比较运算:
-e filename
如果 filename 存在,则为true [ -e /var/log/syslog ]-d filename
如果 filename 为目录,则为true [ -d /tmp/mydir ]-f filename
如果 filename 为常规文件,则为true [ -f /usr/bin/grep ]-L filename
如果 filename 为符号链接1,则为true [ -L /usr/bin/grep ]-r filename
如果 filename 可读,则为true [ -r /var/log/syslog ]-w filename
如果 filename 可写,则为true [ -w /var/tmp.txt ]-x filename
如果 filename 可执行,则为true [ -x /usr/bin/grep ]filename1 -nt filename2
(new than)如果 filename1 比 filename2 新,则为true [ /tmp/install/etc/services -nt /etc/services ]filename1 -ot filename2
(old than)如果 filename1 比 filename2 旧,则为true [ /boot/bzImage -ot arch/i386/boot/bzImage ]几乎所有语言都要实现逻辑true和false的运算,大多语言都用1和0表示true和false。非0的数就是true,比如python中可以这样来统计非0数的个数:
>>> arr=[2,3,4,0,7,0,3]
>>> len([ i for i in arr if i])
5
>>>
shell中虽然实现原理不同,但有类似的写法:
bool=1
if [ $bool ];then echo "YES"; fi
YES
if
后面的方括号表示比较运算,这里没有比较对象。就是和空字符串比较了,所以可以得到正确的结果。但如果bool=" "
,当bool是一个空格时,在大多语言中也表示false,但shell会认为这是一个字符串,打印出YES,可以想象0也是同样的:
bool=" "
if [ "$bool" ];then echo "YES"; fi
YES
所以shell中要进行逻辑运算,需要明确写出比较对象,如[ "$bool" != "" ]
才不容易出错。
还有一种逻辑运算,通常用于连接两个比较,比如要求既...又...才...
的情况,...或者...就...
:
&&
表示和的意思,1 && 1 = 1 ,两边都为true,结果才是true。也用于连接两个命令。||
表示或的意思,1 || 0 = 1,有一边为true,结果就是true。!
表示非的意思,!1 = 0,!0 = 1,就是取相反值。本章的基础知识很重要,想要学会shell,这些基础是必须掌握的。倒不一定要马上背下来,代码多写写,这些自然很快就能掌握的。至于二进制位运算笔者觉得一时半会用不着,而且不好理解,就先放放。
返回专栏目录 <<<
符号链接是Linux中的一种特殊文件,类似于Windows中快捷方式,它分为软链接和硬链接,软件链接它就相当于Windows的快捷方式。硬链接呢会真的生成一个文件,这个链接文件与源文件相同,相当于自动备份功能。用法:ln 源文件名 硬链接名
,ln -s 源文件名 软链接名
??