编程基础 - 数据类型

发布时间:2024年01月10日

编程基础 - 数据类型

返回序言及专栏目录


前言

这个问题对于c/c++来说,那至少要有三五章来说明。对于shell来说那太简单了,严格来说,shell并没有数据类型的概念,无论你输入的是字符串还是数字,都是按照字符串类型来存储的。那么我们来看看shell是怎么用字符串实现和其它编程语言一样的功能的。


一、字符串和数组

  • 定义字符串:a是一个字符,ab是字符串。也可以认为a是只有一个字符的字符串。
  • 字符串可以用单双引号表示,也可以不用,真是太自由了。'a'"a"a是一样的。当然有时是必须有引号的,比如比较运算时。
  • 单引号之内不处理所有运算符,比如$和反引号“`”符在单引号内不被当做运算符,只是字符
  • 双引号内的运算符会被计算,如图所示:
    在这里插入图片描述
    更复杂的计算也是一样的,单引号内套上双引号也没用:
    在这里插入图片描述
  • 用数学的话说:数组是数据的集合,数学中我们表示集合{1,2,3,4,5},{a,b,c,d,e},在shell中也有这种数据。这样写:arr=(a b c d e),不过只能在bash中使用!sh是没有给数组赋值的功能的。我们到bash中演示一下数组,因为sh虽然不能赋值,但可以迭代数组。
    在这里插入图片描述
    用文字再解释一遍,比较容易看明白:
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,这些基础是必须掌握的。倒不一定要马上背下来,代码多写写,这些自然很快就能掌握的。至于二进制位运算笔者觉得一时半会用不着,而且不好理解,就先放放。

返回专栏目录 <<<


  1. 符号链接是Linux中的一种特殊文件,类似于Windows中快捷方式,它分为软链接和硬链接,软件链接它就相当于Windows的快捷方式。硬链接呢会真的生成一个文件,这个链接文件与源文件相同,相当于自动备份功能。用法:ln 源文件名 硬链接名ln -s 源文件名 软链接名 ??

文章来源:https://blog.csdn.net/alal001/article/details/135499278
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。