shell脚本
- shell简介
- 变量
- 数组
- 运算符
- 关系运算符
shell简介
shell是一个命令解释器,提供用户和机器之间的交互。它支持特定的语法,比如逻辑判断、循环。每个用户都可以有自己特定的shell,并且还有其他的shell,如zsh、ksh等。Centos 7默认的shell为bash(Bourne Agin Shell)
CentOS系统中支持很多shell,可以通过查看/etc/shells文件,查看所支持的shell,目前大多数的Linux基本都使用bash。
[root@localhost ~]# cat /etc/shells/bin/sh/bin/bash/usr/bin/sh/usr/bin/bash/bin/tcsh/bin/csh
Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。
Shell也是一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
编译型语言:
优点:编译器一般会有预编译的过程,对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高,可以脱离语言环境独立运行。
缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
代表语言:C、C++、Pascal、Object-C、swift等。
解释型语言:
优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。相对来说比较灵活,比如修改代码的时候可以直接修改,并且可以快速部署,不用停机维护。
缺点:每次运行的时候都要解释一遍,效率较低,性能上不如编译型语言。
代表语言:JavaScript、Python、Erlang、PHP、Perl、Ruby
bash shell有两种工作模式:互动模式和脚本模式。脚本模式效率更高,可以实现自动化。
编写脚本如下:
[root@localhost ~]# vim helloworld.sh#!/bin/bash#this line is a commentecho \"hello world\"
运行脚本的方法:
1.可以用bash去运行
[root@localhost ~]# bash helloworld.shhello world
2.“.”命令也都可以执行脚本,且不需要可执行权限
[root@localhost ~]# . helloworld.shhello world
3.给脚本添加可执行权限,然后直接就可以执行
[root@localhost ~]# chmod +x helloworld.sh[root@localhost ~]# ./helloworld.shhello world
PS: 脚本是各种命令的集合,最终都是要执行服务。
查看各种命令存储的位置:
[root@localhost ~]# echo $PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
变量
变量表示其值是可以变化的量。从变量的本质来说,变量名是指向一片用于存储数据的内存空间。而且变量有局部变量、环境变量之分。在脚本中,往往需要使用变量来存储有用信息,比如文件名、路径名、数值等。通过这些变量可以控制脚本的运行过程。
1.局部变量
指在某个shell中生效的变量,对其他shell来说无效,局部变量的作用域被限定在声明它们的shell中,可以使用local内的命令来“显示”的声明局部变量,但仅限于函数内使用。
2.环境变量
通常又称“全局变量”,在shell脚本中,变量默认就是全局的,为了让子shell继承当前shell的变量,可以使用export命令将其定义为环境变量。
3.环境变量的设置
(1):declare -x
[root@localhost ~]# declare -x class=\"linux base\"[root@localhost ~]# echo $classlinux base
(2)export
[root@localhost ~]# export class1=\"linux\"[root@localhost ~]# echo $class1linux
(3)变量名=变量值
[root@localhost ~]# class2=\"nihao\"[root@localhost ~]# echo $class2nihao
总结:declare -x 和export这种设置变量的方式,是全局的环境变量(特点:子进程会继承父进程的环境变量),第三种\”变量名=变量值\”的方式,默认只在当前进程有效。如果想要子进程也有效,可以使用前两种暴露的方式,或者将变量添加到/etc/profile文件中。
[root@localhost ~]# vim /etc/profile
注意等号左右不能加空格,引号可以是单引号,也可以是双引号,当然也可以是引用一条命令执行后的结果(使用反引号)。
[root@localhost ~]# name=`cat /etc/fstab`[root@localhost ~]# echo $name
使用反应号之后,显示格式变化,如果想要按照原格式显示,那么可以加上双引号。
[root@localhost ~]# echo \"$name\"
其实变量的值也可以是一个变量。
[root@localhost ~]# x1=\"a\"[root@localhost ~]# x2=\"b\"[root@localhost ~]# x3=\"$x1\"[root@localhost ~]# echo \"$x1\"a[root@localhost ~]# echo \"$x3\"a[root@localhost ~]# x1=\"c\"[root@localhost ~]# echo \"$x1\"c[root@localhost ~]# echo \"$x3\"a
从上面可以看出把x1的值改了,x3的值也不会跟着改变。这是因为当时我们设置变量的时候,x3又重新指定了一块\”存储空间\”。和x1的值已经没有关系了。
变量命名法则:
shell中的变量必须以字母或者下划线开头,后面可以跟数字、字母和下划线,长度没有限制,区分大小写。比如:(单驼峰、双驼峰、三驼峰)
变量赋值和取值
定义变量:变量名=变量值
1.注意变量名和变量值之间用等号紧紧相连,之间没有任何空格。
[root@localhost ~]# name=nihao[root@localhost ~]# name= nihao-bash: nihao: 未找到命令
2.注意当变量值中有空格时必须用引号括起,否则会出现错误,可以是双引号,也可以是单引号。
[root@localhost ~]# name=li lei-bash: lei: 未找到命令[root@localhost ~]# name=\"li lei\"
3.变量的取值很简单,在变量名前加上$就可以了。
[root@localhost ~]# echo $nameli lei
PS: 在多数情况下,运行脚本如果提示错误,仔细检查脚本后还是不确定哪里出现了问题,那么这时候不妨将变量的取值写成标准格式,可能就会解决问题。
取消变量:
取消变量使用unset,后面跟变量名。函数也可以被取消,unset后面也可以跟上函数名来取消函数。
[root@localhost ~]# unset name[root@localhost ~]# echo ${name}
PS:在不用变量的时候,我们会取消变量,虽然退出进程也可以取消变量,但一般都会人为的再去取消。
set命令可以显示所有变量:包括局部变量和环境变量。也会显示一些函数。env、printenv、export、declare -x也可以。
特殊变量
上边我们接触到局部变量和环境(全局)变量,也知道了他们特点:局部变量只在当前shell有效,环境变量的有效范围为当前shell和子shell。除了这些还有其他一些变量也需要我们注意。
1.只读变量(常量):
只能声明,但不能修改和删除。
[root@localhost ~]# readonly name1=\"wukong\"[root@localhost ~]# echo $name1wukong[root@localhost ~]# name1=\"bajie\"-bash: name1: 只读变量
还可以使用declare -r 声明常量。
[root@localhost ~]# declare -r name2=\"bajie\"[root@localhost ~]# echo $name2bajie[root@localhost ~]# name2=\"baima\"-bash: name2: 只读变量
查看常量值:readonly:有效期直到是进程结束。
[root@localhost ~]# readonly -p
2.括号的使用
小括号用法:一次性使用,不会对环境产生影响。
[root@localhost ~]# (x=zj;echo $x)zj[root@localhost ~]# echo $x
大括号用法:前后有空格,这个对全局有影响。
[root@localhost ~]# { x=zj;echo $x }zj[root@localhost ~]# echo $xzj
3.位置变量
shell中还有一些预先定义的特殊只读变量,它们的值只有在脚本运行时才能确定。
$0:代表脚本本身名字$1----$9:第一个位置参数----第九个位置参数$#:脚本参数的个数总和$@:表示脚本的所有参数$*:表示脚本的所有参数
例子
[root@localhost ~]# vim t1.sh#!/bin/bashecho \"这个脚本的名字是:$0\"echo \"参数一共有:$#个\"echo \"参数的列表是:$@\"echo \"参数的列表是:$*\"echo \"第一个参数是:$1\"echo \"第二个参数是:$2\"echo \"第三个参数是:$3\"echo \"第十二个参数是:${12}\"
如果想让它识别为12,这个时候就要用到花括号。
[root@localhost ~]# bash t1.sh {1..13}这个脚本的名字是:t1.sh参数一共有:13个参数的列表是:1 2 3 4 5 6 7 8 9 10 11 12 13参数的列表是:1 2 3 4 5 6 7 8 9 10 11 12 13第一个参数是:1第二个参数是:2第三个参数是:3第十二个参数是:12
假设我们要复制一个文件到另一台虚拟机。需要用scp命令。
[root@localhost ~]# cat /etc/redhat-release > myrelease.txt[root@localhost ~]# cat myrelease.txtCentOS Linux release 7.7.1908 (Core)[root@localhost ~]# vim scp.sh#!/bin/bashecho \"start copy...\"scp $* [email protected]:/rootecho \"copy is finished\"[root@localhost ~]# bash scp.sh myrelease.txtstart copy...myrelease.txt 100% 37 26.5KB/s 00:00copy is finished
当然也可以做到免密登录,传输文件时就不要输入密码。
[root@localhost ~]# ssh-keygen -t rsa[root@localhost ~]# ssh-copy-id [email protected][root@localhost ~]# bash scp.sh myrelease.txtstart copy...myrelease.txt 100% 37 28.5KB/s 00:00copy is finished
下面两者的区别:
$*:代表脚本的所有参数,但是会把显示的结果当做一个整体显示。$@:代表脚本的所有参数,把收集到的结果分开来显示。注意:在两者有双引号的前提下,显示效果才会区分,如果没有双引号,显示效果一样。
$0引用名称的使用
如果说把一个脚本添加一个软链接。这个时候再运行脚本,那么它的名称应该是显示原来的名字还是修改后的名字呢?
[root@localhost ~]# ln -s t1.sh link.sh[root@localhost ~]# bash link.sh这个脚本的名字是:link.sh参数一共有:0个...
我们可以利用这一点,即便是同一个脚本,到时候也可以拓展出不同的功能。
shift命令:整体左移
[root@localhost ~]# vim t1.sh#!/bin/bashecho \"这个脚本的名字是:$0\"echo \"参数一共有:$#个\"echo \"参数的列表是:$@\"echo \"参数的列表是:$*\"echo \"第一个参数是:$1\"echo \"第二个参数是:$2\"echo \"第三个参数是:$3\"echo \"第十二个参数是:${12}\"shiftecho \"这个脚本的名字是:$0\"echo \"参数一共有:$#个\"echo \"参数的列表是:$@\"echo \"参数的列表是:$*\"echo \"第一个参数是:$1\"echo \"第二个参数是:$2\"echo \"第三个参数是:$3\"echo \"第十二个参数是:${12}\"shiftecho \"这个脚本的名字是:$0\"echo \"参数一共有:$#个\"echo \"参数的列表是:$@\"echo \"参数的列表是:$*\"echo \"第一个参数是:$1\"echo \"第二个参数是:$2\"echo \"第三个参数是:$3\"echo \"第十二个参数是:${12}\"
PS:当然这是默认的移动一位,当然也可以设置决定移动多少位,在shift后面直接加上想要移动的位数即可。
4.退出状态
进程使用退出状态来报告成功或者失败。
可以用如下命令去验证:
[root@localhost ~]# lst1.sh[root@localhost ~]# echo $?0[root@localhost ~]# ls /abcls: 无法访问/abc: 没有那个文件或目录[root@localhost ~]# ehco $?2
0 代表成功,1~255 代表失败。
当然退出返回码是可以修改的。
[root@localhost ~]# vim t2.sh#!/bin/bashecho \"t2脚本所有参数是:$*\"bash t3.sh \"$@\"exit 10[root@localhost ~]# bash t2.sht2脚本所有参数是:t3脚本的第一个参数:[root@localhost ~]# echo $?10
那么我们就要考虑,根据不同的返回码,我们是不是就可判断出系统的运行状态.假设说,我们规定了计算机会有几种情况,如果说一种情况返回10,另一种情况返回20,还有30,那么我们就可根据系统返回的值的不同来判断出系统肯定是满足了某种条件。这样就可以作为定位判断系统状态的依据,其实在我们上网的时候有返回发404,其实也是类似这种方法的。200 OK 404 Not Found
3xx重定向—————–4xx客户端错误—————–5xx服务器错误
当然exit命令在外部也可以使用,意思是退出bash
数组
数组是一种特殊的数据结构,其中的每一项被称为一个元素,每个元素都可以用索引的方式取出对应的值。使用数组的典型场景是一次性要记录很多类型相同的数据(但不一定必须要相同)。比如记录班级中所有人的数学成绩,如果不用数组来处理,那就只能定义所有人成绩的变量。shell中的数组对元素个数没有限制,但只支持一维数组。
1.定义数组
(1)使用declare命令定义数组
[root@localhost ~]# declare -a list[root@localhost ~]# list[0]=tom[root@localhost ~]# list[1]=jerry[root@localhost ~]# list[2]=dog[root@localhost ~]# echo ${list[0]}tom[root@localhost ~]# echo ${list[1]}jerry[root@localhost ~]# echo ${list[2]}dog
(2)也可以在创建属组的同时赋值
[root@localhost ~]# name=(\"zhangsan\" \"lisi\" \"wangwu\")[root@localhost ~]# echo ${name[0]}zhangsan[root@localhost ~]# echo ${name[1]}lisi[root@localhost ~]# echo ${name[2]}wangwu
也可以用负数表示倒叙
[root@localhost ~]# echo ${name[-3]}zhangsan[root@localhost ~]# echo ${name[-2]}lisi[root@localhost ~]# echo ${name[-1]}wangwu
PS: 注意数组多个元素之间不要用逗号隔开,否则会把多个元素当做整体一起输出。
(3)还可以使用跳号赋值,也就是说可以指定索引号。
[root@localhost ~]# score=([1]=1 [3]=3 [5]=5)[root@localhost ~]# echo ${score[1]}1[root@localhost ~]# echo ${score[3]}3[root@localhost ~]# echo ${score[5]}5
2.数组的取值
默认显示数组第一个元素值
[root@localhost ~]# echo ${name=[0]}zhangsan[root@localhost ~]# echo ${name}zhangsan
取出对应索引下标的值
[root@localhost ~]# echo ${name[2]}wangwu[root@localhost ~]# echo ${name[1]}lisi[root@localhost ~]# echo $[name[0]}zhangsan
PS: 注意数组内索引为0的才是第一个。
3.一次取出数组所有的值
@得到的是以空格隔开的元素
[root@localhost ~]# echo ${name[@]}zhangsan lisi wangwu
*得到的是一整个字符串
[root@localhost ~]# echo ${name[*]}zhangsan lisi wangwu
4.数组的长度:数组元素的个数
使用“#”来获取数组元素的个数。
[root@localhost ~]# echo ${#name[@]}3[root@localhost ~]# echo ${#name[*]}3
5.数组的截取(分片)
可以截取某个元素的一部分,对象可以是整个数组或某个元素。
首先定义数组
[root@localhost ~]# number=(1 2 3 4 5 6 7 8)[root@localhost ~]# echo ${number[*]}1 2 3 4 5 6 7 8[root@localhost ~]# echo ${#number[*]}8
取出第一个到第3个元素的值
[root@localhost ~]# echo ${number[@]:0:3}1 2 3
取出第3个到第6个元素的值
[root@localhost ~]# echo ${number[@]:2:4}3 4 5 6
也可以取某个元素的第几个字符到第几个字符。
[root@localhost ~]# word=(hello world and welcome)[root@localhost ~]# echo ${word[2]:0:3}and[root@localhost ~]# echo ${word[1]:0:3}wor[root@localhost ~]# echo ${word[1]:0:4}worl
总结:左边的数字表示的是下标(从第几个开始截取),右边表示的是截取的个数。
6.连接数组
将若干个数组进行拼接操作,将两个数组的元素全部连接。
[root@localhost ~]# new=(${number[@]} ${word[@]})[root@localhost ~]# echo ${new[@]}1 2 3 4 5 6 7 8 hello world and welcome
7.替换元素
将数组内某个元素的值替换成其他值。
[root@localhost ~]# name=(zhangsan lisi wangwu zhaoliu)[root@localhost ~]# echo ${name[@]}zhangsan lisi wangwu zhaoliu
将lisi替换为lilei
[root@localhost ~]# name=(${name[@]/lisi/lilei})[root@localhost ~]# echo ${name[@]}zhangsan lilei wangwu zhaoliu
取消数组或元素可以使用unset命令,取消数组中的一个元素
[root@localhost ~]# unset name[2][root@localhost ~]# echo ${name[@]}zhangsan lilei zhaoliu
取消整个数组就相当于取消全部变量
[root@localhost ~]# unset name[root@localhost ~]# echo ${name[@]}
转义和引用
转义:shell中有很多特殊字符有特殊意义,但是有时候会造成误解,需要转义才可以使用,转义符号为“\\”。
引用:指将字符串用某种符号括起来,以防止特殊字符被解析为其他意思。shell中一共有4种引用符,分别为双引号、单引号、反引号和转义符。双引号可以引用除$符号、反引号、转义符之外的所有字符;单引号可以引用所有字符;反引号则会将反引号中的内容解释为系统命令。
运算符
shell中的运算符主要有比较运算符(用于整数比较)、字符串运算符(用于字符串测试)、文件操作运算符(用于文件测试)、逻辑运算符、算术运算符、位运算符、自增自减运算符等。
1.算术运算符
算术运算符指的是加、减、乘、除、余、幂等常见的算术运算,以及加等、减等、乘等、除等、余等复合算术运算。要特别注意的是,shell只支持整数计算,也就是说所有可能产生小数的运算都会舍去小数部分。
常规算术运算符-----------------------------------------------------运算符 运算符举例 运算结果+(加运算符) 1+1 2-(减运算符) 2-1 1*(乘运算符) 2*3 6/ (除运算符) 9/4 2%(余运算符) 10%3 1**(幂运算符) 2**3 8------------------------------------------------------复合算术运算符运算符 运算符举例 运算结果+=(加等运算符) x=8;x+=2 10-=(减等运算符) x=8;x-=2 6*=(乘等运算符) x=8;x*=2 16/=(除等运算符) x=8;x/=2 4%=(余等运算符) x=8;x%=2 0
Bash shell的算术运算有四种方式:
(1)使用expr外部程式
[root@localhost ~]# a=`expr 4 + 5`[root@localhost ~]# echo $a9
PS:注意 ‘4’ ‘+’ ‘5’ 这三者之间要有空格
(2)使用 $(( ))
[root@localhost ~]# a=$((4 + 5))[root@localhost ~]# echo $a9
(3)使用 $[ ]
[root@localhost ~]# a=$[4 + 5][root@localhost ~]# echo $a9
(4)使用let命令
[root@localhost ~]# let a=4+5[root@localhost ~]# echo $a9
(5)总结
PS: let命令可以直接用变量名,并且支持变量名和具体数值之间的各种运算。
2.与运算、或运算、异或运算
在说这个之前,先了解一下10进制转2进制。
将一个十进制数除以二,得到的商再除以二,依此类推直到商等于一或零时为止,倒取除得的余数,即换算为二进制数的结果。只需记住要点:除二取余,倒序排列。
二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个乘以2的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。
与运算:&(并且)and
0 false 假1 true 真0&0=00&1=01&0=01&1=1总结:有假必为假,同真才为真。
或运算:|(或者)or
0|0=00|1=11|0=11|1=1总结:有真必为真,同假才为假。
异或运算:^异或
0^1=10^0=01^0=11^1=0总结:同性为假,异性为真。
短路与&&逻辑与
0&&0=00&&1=01&&0=01&&1=1如果是命令:cmd1 && cmd2总结:如果cmd1为假, cmd2不需要执行。反之cmd1为真,需要执行cmd2。
短路或 || 逻辑或
0||0=00||1=11||0=11||1=1cmd1 || cmd2总结:如果cmd1为真,cmd2不需要执行。反之cmd1为假,需要执行cmd2。
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
-eq:检测两个数是否相等,相等返回true。[ $a -eq $b ]返回false。-ne:检测两个数是否不相等,不相等返回true。[ $a -ne $b ]返回true。-gt:检测左边的数是否大于右边的,如果是,则返回true。[ $a -gt $b ]返回false。-lt:检测左边的数是否小于右边的,如果是,则返回true。[ $a -lt $b ]返回true。-ge:检测左边的数是否大于等于右边的,如果是,则返回true。[ $a -ge $b ]返回false。-le:检测左边的数是否小于等于右边的,如果是,则返回true。[ $a -le $b ]返回true。
例子:
如果参数1大于参数2,那么两数相减,并输出结果值“因为参数1大于参数2,所以两数相减,结果值为:”
如果参数1等于参数2,那么两数想乘,并输出结果值“因为参数1等于参数2,所以两数相乘,结果值为:”
如果参数1小于参数2,那么两数想加,并输出结果值“因为参数1小于参数2,所以两数相加,结果值为:”
[root@localhost ~]# vim bijiao.sh#!/bin/bashif [ $1 -gt $2 ]thenecho \"参数$1 大于 $2 ,所以两数相减等于:`expr $1 - $2`\"fiif [ $1 -eq $2 ]thenecho \"参数$1 等于 $2,所以两数相乘等于:`expr $1 \\* $2`\"fiif [ $1 -lt $2 ]thenecho \"参数$1 小于 $2 ,所以两数相加等于:`expr $1 + $2`\"fi