什么是shell
shell是一个命令解释器,提供用户和机器之间的交互
支持特定语法,比如逻辑判断、循环每个用户都可以有自己特定的shell还有其他zsh、ksh等
CentOS7 默认的shell为bash(Bourne Agin Shell) ,Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。
Shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
shell是一种解释型语言,这种语言经过编写后不经过任何编译就可以执行,是一种脚本语言。和编译型语言是执行前翻译不同,解释型语言的程序是执行时翻译,所以效率要差一些。
1、编译型语言和解释型语言:
优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。
缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
代表语言:C、C++、Pascal、Object-C、swift等。
CentOS系统中支持很多shell,可以通过查看/etc/shells文件,查看所支持的shell,目前大多数的Linux基本都使用bash
[root@base ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
解释型语言
解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。每一个语句都是执行的时候才能翻译。这样解释性语言每执行一次要翻译一次,效率表较低。
优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。
缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。
代表语言:JavaScript、Python、Erlang、PHP、Perl、Ruby
bash shell有两种工作模式:互动模式和脚本模式。脚本模式效率更高,可以实现自动化。
编写第一个shell脚本:
[root@base shell]# cat Helloworld.sh
#!/bin/bash //是一个标记,告诉系统执行这个文件需
要的解释器
#this line is a comment // “#”号开头的行代表注释
echo "Hello world
运行脚本有两种方法:
- 使用bash命令执行
[root@base shell]# bash Helloworld.sh
Hello world
// “.”命令也都可以执行脚本,且不需要可执行权限
[root@base shell]# . Helloworld.sh
Hello world
- 给脚本添加可执行权限,然后直接就可以执行了
[root@base shell]# chmod +x Helloworld.sh
[root@base shell]# ./Helloworld.sh
Hello world
PS: 脚本是各种命令的集合,终究还是要执行服务的。
- 查看命令存储路径
[root@base bin]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@base bin]# cat test.sh
#!/bin/bash
ls /etc/passwd
mkdir /data
cp /etc/passwd /data/
[root@base bin]# chmod +x test.sh
[root@base bin]# test.sh
变量
顾名思义,变量就是其值可以变化的量。从变量的本质来说,变量名是 指向一片用于存储数据的内存空间。变量有局部变量、环境变量之分。在脚本中,往往需要使用变量来存储有用信息,比如文件名、路径名、数值等,通过这些变量可以控制脚本的运行行为。
1、局部变量:是指在某个shell中生效的变量,对其他shell来说无效,局部变量的作用域被限定在声明它们的shell中,可以使用local内建命令来“显式”的声明局部变量,但仅限于函数内使用。
2、环境变量通常又称“全局变量”,以区别于局部变量。在shell脚本中,变量默认就是全局的,为了让子shell继承当前shell的变量,可以使用export命令将其定义为环境变量
$PATH UID HOSTNAME PWD (存在内存中的)
3、设置环境变量:
第一种: declare -x
[root@base shell]# declare -x class="Linux base"
[root@base shell]# echo $class
Linux base
第二种: export
[root@base shell]# export class1="Linux"
[root@base shell]# echo $class1
Linux
第三种:
[root@base shell]# name="bdqn"
[root@base shell]# echo $name
Bdqn
总结: declare -x 和export这种设置变量的方式,是全局的环境变量,(全局环境变量的特点: 子进程会继承父进程的环境变量),第三种"变量名=变量值"的这种方式,默认只在当前进程有效,如果想要子进程也有效,可以使用前两种暴露的方式,或者,将变量添加到/etc/profile文件中
[root@base shell]# source /etc/profile
[root@base shell]# echo $name
zhaoliu
注意等号左右不能加空格,引号可以是单引号,也可以是双引号,当然也可以是引用一条命令执行后的结果(使用反引号)。
[root@base shell]# name=`cat /etc/fstab`
[root@base shell]# echo $name
//使用反应号之后,显示格式会变化,变成了一行了。那么怎么能够显示正常那?
[root@base shell]# echo "$name"
//那么同样还有,变量的值也可以是一个变量。
[root@base shell]# name1=bdqn
[root@base shell]# name2=yjs
[root@base shell]# name3=$name1
[root@base shell]# echo $name3
Bdqn
那么问题来了,如果我们现在把name1的值改了,name3的值是否也会跟着改变那? 不会
[root@base shell]# name1=base
[root@base shell]# echo $name3
bdqn
命名法则举例:
studentname====>大驼峰StudentName,小驼峰:studentName(单驼
峰 双驼峰 三驼峰)
变量命名
shell中的变量必须以字母或者下划线开头,后面可以跟数字、字母和下划线,长度没有限制,区分大小写。 "见名知意"
变量赋值和取值
定义变量:变量名=变量值
注意点1:变量名和变量值之间用等号紧紧相连,之间没有任何空格
[root@base shell]# name=john
[root@base shell]# name= john
-bash: john: command not found
注意点2:当变量值中有空格时必须用引号括起,否则会出现错误,可以是双引号,也可以是单引号
[root@base shell]# name=li lei
-bash: lei: command not found
变量的取值很简单,在变量名前加$号就可以了,严谨的方法是${}。建议用后者
[root@base shell]# echo $name
li lei
[root@base shell]# echo ${name}
li lei
PS: 这里注意,在很多时候,运行脚本会提示错误,仔细检查脚本后还是不确定哪里出现了问题,那么这时候不妨将变量的取值写成标准格式,说不定就会有惊喜。
取消变量
取消变量使用unset,后面跟变量名。函数也是可以被取消的,unset后面也是可以跟上函数名来取消函数的。
[root@base shell]# unset name
[root@base shell]# echo ${name}
PS:当然一般在不用变量的时候,我们都会取消变量,当然退出进程也可以取消变量,但一般我们都会人为的再去取消一下用uset 命令
//这里注意一下我unset 的时候没有用$符号,一般引用的时候加$符号,也就是说前面的命令如果识别不出来变量的时候,就像我们用echo的时候,如果你不加$符号,他会把后面的当做字符串
set命令可以显示所有变量:包括局部变量和环境变量。也会显示一些
函数。env、printenv、export、declare -x也可以。
特殊变量
上边我们接触到局部变量和环境(全局)变量,也知道了他们特点:局部变量只在当前shell有效,环境变量的有效范围为当前shell和子shell。除了这些还有其他一些变量,也需要我们注意
1、只读变量(常量):
只能声明,但不能修改和删除
[root@base shell]# readonly name1="wukong"
[root@base shell]# echo $name1
wukong
[root@base shell]# name1=bajie
-bash: name1: readonly variable
还可以使用declare -r 声明常量。
[root@base shell]# declare -r name2="bajie"
[root@base shell]# echo $name2
bajie
readonly:有效期直到是进程结束。那么这个只读变量有什么用那?比方说 pi=3.14 3.1415926
//查看常量值
[root@base ~]# readonly -p
2、括号的使用
小括号用法:一次性使用。不会对环境产生影响
[root@base shell]# (name=yjs;echo $name)
yjs
[root@base shell]# echo $name
大括号:前后有空格。这个是对全局有影响的。
[root@base shell]# { name=adam; echo $name; }
adam
[root@base shell]# echo $name
adam
3、位置变量
shell中还有一些预先定义的特殊只读变量,它们的值只有在脚本运行时才能确定。
$0:代表脚本本身名字
$1----$9:第一个位置参数-------第9个位置参数
$#:脚本参数的个数总和
$@:表示脚本的所有参数
$* :表示脚本的所有参数
[root@base shell]# cat example.sh
#!/bin/bash
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
[root@base shell]# bash example.sh 1 2 3 4 5
这个脚本的名字是:example.sh
参数一共有:5个
参数的列表是: 1 2 3 4 5
参数的列表是: 1 2 3 4 5
第一个参数是: 1
第二个参数是: 2
第三个参数是:3
//如果有10个以上的数改怎么表示
[root@base shell]# echo {1..15}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[root@base shell]# echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
怎么才能让它识别为$10那?这个时候就要用到花括号了。
[root@base shell]# cat example.sh
#!/bin/bash
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
[root@base shell]# bash example.sh {1..15}
这个脚本的名字是:example.sh
参数一共有:15个
参数的列表是: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
参数的列表是: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
第一个参数是: 1
第二个参数是: 2
第三个参数是:3
第12个参数是:12
//那么具体这个有什么用那?,其实可以这样,假设我们要复制一个文件到另一台虚拟机。用什么命令那?scp、
[root@base shell]# cat /etc/redhat-release >
mylrelease.txt
[root@base shell]# cat mylrelease.txt
CentOS Linux release 7.6.1810 (Core)
//可以用脚本来写了
[root@base shell]# cat scp.sh
#!/bin/bash
echo "start copy..."
scp $1 root@192.168.1.61:/root/
echo "copy is finished"
[root@base shell]# bash scp.sh mylrelease.txt
start copy...
root@192.168.1.61's password:
mylrelease.txt 100%
38 22.6KB/s 00:00
copy is finished
//免密登录。传东西不用输入密码。
[root@base shell]# ssh-keygen -t ecdsa
Ssh-copy-id -i ~/.ssh/id_ecdsa.pub root@192.168.1.15
输入另一台虚拟机密码然后切换到另一台虚拟机
[root@base shell]# ssh-keygen -t rsa
//那么如果要传送多个文件,这个脚本改怎么写那?是不是把$1 改成$* 啊?那么用sed语句应该咋么写?
[root@base shell]# sed -i 's/$1/$*/' scp.sh
[root@base shell]# cat scp.sh
[root@base shell]# bash scp.sh /etc/fstab /tmp/*
$* 和 $@ 的区别。
$*和$@都代表了脚本的所有参数,但是,$*它会把显示的结果当做一个整体显示,而$@会把收集到的结果分开来显示。不过要注意,如果$*和$@有双引号的前提下,显示效果会做区分,如果都没有双引号,
效果一样。
//理论上,t2脚本的第一个参数,显示的并不正确
[root@base shell]# cat t1.sh
#!/bin/bash
echo "t1脚本所有参数是:$*"
bash t2.sh "$*"
[root@base shell]# cat t2.sh
#!/bin/bash
echo "t2脚本的第一个参数:$1"
[root@base shell]# bash t1.sh 1 2 3
t1脚本所有参数是:1 2 3
t2脚本的第一个参数:1 2 3
//把t1.sh中的 bash t2.sh “$*” 换成 $@ 用sed 怎么换。
[root@base shell]# sed -i '3s/\$\*/\$\@/' t1.sh
[root@base shell]# bash t1.sh 1 2 3
t1脚本所有参数是:1 2 3
t2脚本的第一个参数:1
$0 引用名称的使用
再来说这个$0,$0是名称,那么如果说,我把一个脚本,添加一个软连接。这个时候如果再运行脚本,那么它的$0显示的名字应该是原来的名字那?还是更改过后的名字?
[root@base shell]# ln -s example.sh link.sh
[root@base shell]# bash link.sh
这个脚本的名字是:link.sh
参数一共有:0个
参数的列表是:
....
那么以后我们就可以利用这一点,即便是同一个脚本,到时候也可以拓展出不同的功能。比如说,就可以利用这一点。那么就可以让他们返回的值是不一样的。这个就是以后在写脚本的时候,就要注意下,需要对$0做判断了。
shift命令: 整体往左移位
[root@base shell]# cat example.sh
#!/bin/bash
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
shift 2
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
shift 2
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
PS :当然这是默认的移动一位,当然也可以通过设置决定移动多少位,在 shift 后边直接加上想要移动的位数即可。
- 退出状态
进程使用退出状态来报告成功或者失败:
$? 变量保存最近的命令退出状态
0 代表成功,1~255 代表失败
举例:
[root@base shell]# ls /tmp/
[root@base shell]# echo $?
0
当然判断命令是否执行成功,不仅仅是简单的判断$?还有更严谨的判断语句,这个我们以后会讲,现在先知道就可以了。那么在这里还需要知道一 点。我们的脚本里边,往往是大于一条命令的,这个$?返回的0值,如果返回的是0,那么这个0值到底是返回的哪一条命令的值那?
[root@base shell]# cat test1.sh
#!/bin/bash
hostname
[root@base shell]# bash test1.sh
base
[root@base shell]# echo $?
0
PS : 那么我们就要考虑,根据不同的返回码,我们是不是就可判断出系统的运行状态啊?假设说,我们规定了计算机会有几种情况,如果说一种情况返回10,另一种情况返回20,还有30,那么我们就可根据系统返回的值的不同,来判断出,系统肯定是满足了某种条件。这样就可以作为定位判断系统状态的依据,其实在我们上网的时候有返回发404,其实也是类似这种方法的。200 OK 404 Not Found
3xx重定向 4xx客户端错误 5xx服务器错误
当然exit命令在外部也可以使用,意思是退出bash
数组
数组是一种特殊的数据结构,其中的每一项被称为一个元素,对于每个元素,都可以用索引的方式取出元素的值。使用数组的典型场景是一次性要记录很多类型相同的数据时(但不一定必须要相同)。比如,为了记录班级中所有人的数学成绩,如果不用数据来处理,那就只能定义所有人成绩的变量。shell中的数组对元素个数没有限制,但只支持一维数组。
- 定义数组
//使用declare命令定义数组
[root@base shell]# declare -a List
[root@base shell]# List[0]=Tom
[root@base shell]# List[1]=Jerry
[root@base shell]# List[2]=Dog
[root@base shell]# echo ${List[0]}
Tom
[root@base shell]# echo ${List[1]}
Jerry
[root@base shell]# echo ${List[2]}
Dog
//当然,也可以在创建数组的同时赋值,declare -a也可以省略不写
[root@base shell]# Name=("zhangsan" "li si" "wang wu")
[root@base shell]# echo ${Name[0]}
zhangsan
[root@base shell]# echo ${Name[1]}
li si
[root@base shell]# echo ${Name[2]}
wang wu
//也可以用负数,表示倒叙
[root@base shell]# echo ${Name[-1]}
wang wu
[root@base shell]# echo ${Name[-2]}
li si
[root@base shell]# echo ${Name[-3]}
Zhangsan
PS: 注意数组多个元素之间,不要用符号,否则会把多个元素当做整体一起输出。
[root@base shell]# name=("zhangsan","li si","wang wu")
[root@base shell]# echo $name
zhangsan,li si,wang wu
[root@base shell]# echo ${name[0]}
zhangsan,li si,wang wu
[root@base shell]# echo ${name[1]}
//还可以使用跳号赋值,也就是说可以指定索引号。
[root@base shell]# Score=([3]=3 [5]=5 [7]=7)
[root@base shell]# echo ${Score[3]}
3
[root@base shell]# echo ${Score[5]}
5
[root@base shell]# echo ${Score[7]}
7
- 数组的取值
数组取值:格式为:${数组名[索引]}
默认显示数字第一个元素值
[root@base shell]# echo ${Name[0]}
zhangsan
[root@base shell]# echo ${Name}
Zhangsan
取出对应索引下标的值
[root@base shell]# echo ${Name[2]}
wang wu
[root@base shell]# echo ${Name[1]}
li si
[root@base shell]# echo ${Name[0]}
Zhangsan
PS: 注意的是,数组内索引为0 的才是第一个。
- 一次取出数组所有的值
@ //得到的是以空格隔开的元素
[root@base shell]# echo ${Name[@]}
zhangsan li si wang wu
* //得到的是一整个字符串
[root@base shell]# echo ${Name[*]}
zhangsan li si wang wu
- 数组长度: 即数组元素个数
利用“@”或“*”字符,可以将数组扩展成列表,然后使用“#”来获取数组元素的个数。
[root@base shell]# echo ${#Name[*]}
3
[root@base shell]# echo ${#Name[@]}
3
- 数组的截取(分片)(切片)
可以截取某个元素的一部分,对象可以是整个数组或某个元素。(数组的切片:右不包括行为)
//定义数组
[root@base shell]# Number=(1 2 3 4 5 6 7 8)
[root@base shell]# echo ${Number[*]}
1 2 3 4 5 6 7 8
[root@base shell]# echo ${#Number[*]}
8
//取出第一个到第3个元素的值
[root@base shell]# echo ${Number[@]:0:3}
1 2 3
//取出第3个到第6个元素的值
[root@base shell]# echo ${Number[@]:2:4}
3 4 5 6
PS: 到这里,同学们可能就会有点懵了,需要大家注意的是,数组分片的书写格式的意义表示:从第几个开始连续出现几个字符。
//也可以取出某个元素的第几个开始到第n个字符
[root@base shell]# Word=(hello world and welcome)
[root@base shell]# echo ${Word[2]:0:3}
and
[root@base shell]# echo ${Word[1]:0:3}
wor
[root@base shell]# echo ${Word[1]:0:4}
Worl
- 连接数组
将若干个数组进行拼接操作
[root@base shell]# echo ${Number[@]}
1 2 3 4 5 6 7 8
[root@base shell]# echo ${Word[@]}
hello world and welcome
//将两个数组的元素全部连接
[root@base shell]# NewNum=(${Number[@]} ${Word[@]})
[root@base shell]# echo $NewNum
1
[root@base shell]# echo ${NewNum[@]}
1 2 3 4 5 6 7 8 hello world and welcome
//那么现在我们重组新的数组,截取Word数组的第2个元素从第0个连续5个字符,和Number数组里的第2个字符开始连续3个字符。
[root@base shell]# New=(${Word[2]:0:5} ${Number[@]:1:3})
[root@base shell]# echo ${New[@]}
and 2 3 4
- 替换元素
将数组内某个元素的值替换成其他值。如果匹配不到则没有改动
[root@base shell]# Name=(zhangsan lisi wangwu zhaoliu)
[root@base shell]# echo ${Name[@]}
[root@base shell]# echo ${Name[@]}
zhangsan lisi wangwu zhaoliu
//将lisi替换为lilei
[root@base shell]# Name=(${Name[@]/lisi/lilei})
[root@base shell]# echo ${Name[@]}
zhangsan lilei wangwu zhaoliu
//将zhaoliu替换为zhaosi
[root@base shell]# Name=(${Name[@]/zhaoliu/zhaosi})
[root@base shell]# echo ${Name[@]}
zhangsan lilei wangwu zhaosi
//上边的写法可以达到要求,但较为复杂,其实可以完全按照“变量” 重新赋值的方式,直接写数组的对应的元素的值。比如,上述例子中将zhangsan改为 wukong,就可以简化为
[root@base ~]# Name[0]=wukong
[root@base ~]# echo ${Name[@]}
wukong lilei wangwu zhaosi
//取消数组或元素
使用unset命令,取消数组中的一个元素
[root@base shell]# unset Name[2]
[root@base shell]# echo ${Name[@]}
zhangsan lilei zhaosi
//取消整个数组,=取消变量全部
[root@base shell]# unset Name
[root@base shell]# echo ${Name[@]}
数组练习
1、设置两个变量组,groupA和groupB.groupA的成员包括:zhangsan 、lisi、wangwu、zhaoliu
groupB的成员包括:gangtiexia、zhizhuxia、heiguafu、lvjuren、
meidui
[root@shell bin]# groupA=(zhangsan lisi wangsu zhaoliu)
[root@shell bin]# groupB=(gangtiexia zhizhuxia heiguafu
lvjuren meidui)
2、显示每个组的全部成员:
[root@shell bin]# echo ${groupA[@]}
zhangsan lisi wangsu zhaoliu
[root@shell bin]# echo ${groupB[@]}
gangtiexia zhizhuxia heiguafu lvjuren meidui
3、查看groupA的第2个成员。并查看groupB的第3个成员。
[root@shell bin]# echo ${groupA[1]}
lisi
[root@shell bin]# echo ${groupB[2]}
heiguafu
4.将groupA的第4个成员替换为zhaosi,groupB的第一个成员替换为 heibao.
[root@shell bin]# echo ${groupA[@]}
zhangsan lisi wangsu zhaoliu
[root@shell bin]# groupA[3]=zhaosi
[root@shell bin]# echo ${groupA[@]}
zhangsan lisi wangsu zhaosi
[root@shell bin]# groupB[0]=heibao
[root@shell bin]# echo ${groupB[@]}
heibao zhizhuxia heiguafu lvjuren meidui
- 抽取groupA中的成员,和groupB中成员,组成groupC
[root@shell bin]# groupC=(${groupA[@]} ${groupB[@]})
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi wangsu zhaosi heibao zhizhuxia heiguafu
lvjuren meidui
6、显示groupC的全部成员,并且将第8个成员替换为liuneng
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi wangsu zhaosi heibao zhizhuxia heiguafu
lvjuren meidui
[root@shell bin]# groupC[7]=liuneng
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi wangsu zhaosi heibao zhizhuxia heiguafu
liuneng meidui
7、显示groupC的倒数第二个成员。
[root@shell bin]# echo ${groupC[-2]}
Liuneng
8、显示groupC中第二个元素开始到第五个元素。
[root@shell bin]# echo ${groupC[@]:1:4}
lisi zhaosi heibao zhizhuxia
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi zhaosi heibao zhizhuxia heiguafu liuneng
meidui
转义和引用
shell中有很多特殊字符,会有特殊意义,但是有时候会造成麻烦,需要转义才可以使用,转义符号为“\”
[root@base shell]# a=$Dollar
[root@base shell]# echo $a
[root@base shell]# a=\$Dollar
[root@base shell]# echo $a
$Dollar
引用是指将字符串用某种符号括起来,以防止特殊字符被解析为其他意思。shell中一共有4种引用符,分别为双引号、单引号、反引号和转义符。
双引号可以引用除$符号、反引号、转义符之外的所有字符;单引号可以引用所有字符;反引号则会将反引号中的内容解释为系统命令。
root@base shell]# who am i
root pts/0 2020-05-12 00:03 (192.168.1.1)
[root@base shell]# echo "who am i"
who am i
[root@base shell]# echo 'who am i'
who am i
[root@base shell]# echo `who am i`
root pts/0 2020-05-12 00:03 (192.168.1.1)
[root@base shell]# echo $PWD
/opt/shell
[root@base shell]# echo "$PWD"
/opt/shell
[root@base shell]# echo '$PWD'
$PWD
[root@base shell]# echo `$PWD`
bash: /opt/shell: Is a directory
运算符
shell中的运算符主要有比较运算符(用于整数比较)、字符串运算符(用于字符串测试)、文件操作运算符(用于文件测试)、逻辑运算符、算术 运算符、位运算符、自增自减运算符等。
1、算术运算符
算术运算符指的是加、减、乘、除、余、幂等常见的算术运算,以及加等、减等、乘等、除等、余等复合算术运算。要特别注意的是,shell只支 持整数计算,也就是说所有可能产生小数的运算都会舍去小数部分。
/ 除法
% 取模-返回除法的余数
余数是整数除法中被除数未被除尽部分,余数的取值范围为0-除数(不包括除数)的整数
eg:27%6 商数为4,余数为3
若被除数小于除数,则商为0,余数为被除数本身。
Bash shell 的算术运算有四种方式:
1:使用 expr 外部程式
[root@shell ~]# a=`expr 4 + 5`
[root@shell ~]# echo $a
9
PS:注意! '4' '+' '5' 这三者之间要有空白
[root@shell ~]# a=`expr 4+5`
[root@shell ~]# echo $a
4+5
[root@shell ~]# a=`expr 4 * 5`
expr: 语法错误
[root@shell ~]# a=`expr 4 \* 5`
[root@shell ~]# echo $a
20
2:使用 $(( ))
[root@shell ~]# a=$((4 + 5))
[root@shell ~]# echo $a
9
3:使用 $[ ]
[root@shell ~]# a=$[4 + 5]
[root@shell ~]# echo $a
9
4:使用let 命令
[root@shell ~]# m=2
[root@shell ~]# n=3
[root@shell ~]# let a=m+n
[root@shell ~]# echo $a
5: 总结
总结:4种写法的算术运算,例子以数字2、3举例
PS: let命令可以直接用变量名,并且支持变量名和具体数值之间的各种运算。
[root@shell ~]# cat yunsuan.sh
#!/bin/bash
jia=`expr $1 + $2`
echo "参数相加 : $jia"
jian=`expr $1 - $2`
echo "参数相减 :$jian"
cheng=`expr $1 \* $2`
echo "参数相乘 :$cheng"
chu=`expr $1 / $2`
echo "参数相除:$chu"
qumo=`expr $1 % $2`
echo "参数取模:$qumo"
if [ $1 == $2 ]
then
echo "两个参数相等"
fi
if [ $1 != $2 ]
then
echo "两个参数不相等"
fi
PS: 乘号(*)前边必须加反斜杠()才能实现乘法运算;
if...then...fi 是条件语句,后续将会讲解。
2、与运算、或运算、非运算
在说这个之前,先来说一下10进制转2进制。
将一个十进制数除以二,得到的商再除以二,依此类推直到商等于一或零时为止,倒取除得的余数,即换算为二进制数的结果。只需记住要点:除二取余,倒序排列。
二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个乘以2 的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。
与运算:&(并且)and
0 false 假
1 true 真
0&0=0
0&1=0
1&0=0
1&1=1
或运算:| 或者 or
0|0=0
0|1=1
1|0=1
1|1=1
非运算:^
(一山不容二虎,除非一公一母。。。)
0^1=1
0^0=0
1^0=1
1^1=0
[root@shell ~]# let "c=100&125"
[root@shell ~]# echo $c
100
[root@shell ~]# let "c=100|125"
[root@shell ~]# echo $c
125
[root@shell ~]# let "c=100^125"
[root@shell ~]# echo $c
25
短路与 && 逻辑与
0&&0=0
0&&1=0
1&&0=0
1&&1=1
如果是命令:
cmd1 && cmd2
如果cmd1为假, cmd2不需要执行。反之cmd1为真,需要执行cmd2
拿到命令里来说: 前边执行成功,后边的才会执行
短路或 || 逻辑或
0||0=0
0||1=1
1||0=1
1||1=1
cmd1 || cmd2
如果cmd1为真,cmd2不需要执行。反之cmd1为假,需要执行cmd2.
那么这个时候就来想一下,如果我想要添加用户,想要实现,如果没有此用户,我就添加,应该怎么来实现。
[root@shell ~]# id user1 && useradd user1
id: user1: no such user
[root@shell ~]# id user1 && useradd user1
id: user1: no such user
[root@shell ~]# id user1 || useradd user1
id: user1: no such user
[root@shell ~]# id user1 || useradd user1
uid=1000(user1) gid=1000(user1) groups=1000(user1)
那么再来想一下,修改用户user2的密码,如果有用户直接修改,如果没有先添加用户
[root@base ~]# echo 123.com | passwd --stdin user2 ||
useradd user2
passwd: Unknown user name 'user2'.
[root@base ~]# echo 123.com | passwd --stdin user2 ||
useradd user2
Changing password for user user2.
passwd: all authentication tokens updated successfully.
练习:
定义两个数组groupA和 groupB,groupA中的元素分别为:zhangsan lisi wangwu zhaoliu,
groupB中的元素分别为:gangtiexia zhizhuxia heiguafu lvjuren
meidui。写出一个脚本。
判断两个参数是否相等,如果相等则打印出“元素数量相等”,如果不相等则打印“元素数量不相等”
groupA\groupB\元素的数量相加等于多少
groupA\groupB\元素的数量相减等于多少
groupA\groupB\元素的数量相乘等于多少
groupA\groupB\元素的数量相除等于多少
groupA\groupB\元素的数量取模是多少
groupA\groupB\元素的数量等于多少
[root@base ~]# cat jiaoben1.sh
#!/bin/bash
if [ $1 == $2 ]
then
echo "两个参数相等"
fi
if [ $1 != $2 ]
then
echo "两个参数不相等"
fi
xiangjia=`expr $1 + $2`
echo "参数相加:$xiangjia"
xiangjian=`expr $1 - $2`
echo "参数相减:$xiangjian"
xiangcheng=`expr $1 \* $2`
echo "参数想乘:$xiangcheng"
xiangchu=`expr $1 / $2`
echo "参数相除:$xiangchu"
qumo=`expr $1 % $2`
echo "参数取模:$qumo"
[root@base ~]# groupA=(zhangsan lisi wangwu zhaoliu)
[root@base ~]# groupB=(gangtiexia zhizhuxia heiguafu
lvjuren meidui)
[root@base ~]# a=`echo ${#groupA[@]}`
[root@base ~]# b=`echo ${#groupB[@]}`
[root@base ~]# bash jiaoben1.sh $a $b
两个参数不相等
参数相加:9
参数相减:-1
参数想乘:20
参数相除:0
参数取模:4
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
-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。
PS: eq: equql平等的,相等的
gt: greater较大的,大于
lt: less then 小于
举例脚本:
#!/bin/bash
if [ $1 -eq $2 ]
then
echo "$1 -eq $2 : $1 等于 $2"
else
echo "$1 -eq $2 : $1 不等于 $2"
fi
if [ $1 -ne $2 ]
then
echo "$1 -ne $2 : $1 不等于 $2"
else
echo "$1 -ne $2 : $1 等于 $2"
fi
if [ $1 -gt $2 ]
then
echo "$1 -gt $2 : $1 大于 $2"
else
echo "$1 -gt $2 : $1 不大于 $2"
fi
if [ $1 -lt $2 ]
then
echo "$1 -lt $2 : $1 小于 $2"
else
echo "$1 -lt $2 : $1 不小于 $2"
fi
if [ $1 -ge $2 ]
then
echo "$1 -ge $2 : $1 大于或等于 $2"
else
echo "$1 -ge $2 : $1 小于 $2"
fi
if [ $1 -le $2 ]
then
echo "$1 -le $2 : $1 小于或等于 $2"
else
echo "$1 -le $2 : $1 大于 $2"
fi
练习写一个脚本:
判断两个参数的大小,要求:
如果参数1大于参数2,那么两数相减,并输出结果值“因为参数1大于参数
2,所以两数相减,结果值为:”
如果参数1等于参数2,那么两数想乘,并输出结果值“因为参数1等于参数
2,所以两数相乘,结果值为:”
如果参数1小于参数2,那么两数想加,并输出结果值“因为参数1小于参数
2,所以两数相加,结果值为:”
#!/bin/bash
if [ $1 -gt $2 ]
then
echo "参数$1 大于 $2 ,所以两数相减等于:`expr $1 - $2`"
fi
if [ $1 -eq $2 ]
then
echo "参数$1 等于 $2,所以两数相乘等于:`expr $1 \* $2`"
fi
if [ $1 -lt $2 ]
then
echo "参数$1 小于 $2 ,所以两数相加等于:`expr $1 + $2`"
fi
什么是shell
shell是一个命令解释器,提供用户和机器之间的交互
支持特定语法,比如逻辑判断、循环每个用户都可以有自己特定的shell还有其他zsh、ksh等
CentOS7 默认的shell为bash(Bourne Agin Shell) ,Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。
Shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
shell是一种解释型语言,这种语言经过编写后不经过任何编译就可以执行,是一种脚本语言。和编译型语言是执行前翻译不同,解释型语言的程序是执行时翻译,所以效率要差一些。
1、编译型语言和解释型语言:
优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。
缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
代表语言:C、C++、Pascal、Object-C、swift等。
CentOS系统中支持很多shell,可以通过查看/etc/shells文件,查看所支持的shell,目前大多数的Linux基本都使用bash
[root@base ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
解释型语言
解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。每一个语句都是执行的时候才能翻译。这样解释性语言每执行一次要翻译一次,效率表较低。
优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。
缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。
代表语言:JavaScript、Python、Erlang、PHP、Perl、Ruby
bash shell有两种工作模式:互动模式和脚本模式。脚本模式效率更高,可以实现自动化。
编写第一个shell脚本:
[root@base shell]# cat Helloworld.sh
#!/bin/bash //是一个标记,告诉系统执行这个文件需
要的解释器
#this line is a comment // “#”号开头的行代表注释
echo "Hello world
运行脚本有两种方法:
- 使用bash命令执行
[root@base shell]# bash Helloworld.sh
Hello world
// “.”命令也都可以执行脚本,且不需要可执行权限
[root@base shell]# . Helloworld.sh
Hello world
- 给脚本添加可执行权限,然后直接就可以执行了
[root@base shell]# chmod +x Helloworld.sh
[root@base shell]# ./Helloworld.sh
Hello world
PS: 脚本是各种命令的集合,终究还是要执行服务的。
- 查看命令存储路径
[root@base bin]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@base bin]# cat test.sh
#!/bin/bash
ls /etc/passwd
mkdir /data
cp /etc/passwd /data/
[root@base bin]# chmod +x test.sh
[root@base bin]# test.sh
变量
顾名思义,变量就是其值可以变化的量。从变量的本质来说,变量名是 指向一片用于存储数据的内存空间。变量有局部变量、环境变量之分。在脚本中,往往需要使用变量来存储有用信息,比如文件名、路径名、数值等,通过这些变量可以控制脚本的运行行为。
1、局部变量:是指在某个shell中生效的变量,对其他shell来说无效,局部变量的作用域被限定在声明它们的shell中,可以使用local内建命令来“显式”的声明局部变量,但仅限于函数内使用。
2、环境变量通常又称“全局变量”,以区别于局部变量。在shell脚本中,变量默认就是全局的,为了让子shell继承当前shell的变量,可以使用export命令将其定义为环境变量
$PATH UID HOSTNAME PWD (存在内存中的)
3、设置环境变量:
第一种: declare -x
[root@base shell]# declare -x class="Linux base"
[root@base shell]# echo $class
Linux base
第二种: export
[root@base shell]# export class1="Linux"
[root@base shell]# echo $class1
Linux
第三种:
[root@base shell]# name="bdqn"
[root@base shell]# echo $name
Bdqn
总结: declare -x 和export这种设置变量的方式,是全局的环境变量,(全局环境变量的特点: 子进程会继承父进程的环境变量),第三种"变量名=变量值"的这种方式,默认只在当前进程有效,如果想要子进程也有效,可以使用前两种暴露的方式,或者,将变量添加到/etc/profile文件中
[root@base shell]# source /etc/profile
[root@base shell]# echo $name
zhaoliu
注意等号左右不能加空格,引号可以是单引号,也可以是双引号,当然也可以是引用一条命令执行后的结果(使用反引号)。
[root@base shell]# name=`cat /etc/fstab`
[root@base shell]# echo $name
//使用反应号之后,显示格式会变化,变成了一行了。那么怎么能够显示正常那?
[root@base shell]# echo "$name"
//那么同样还有,变量的值也可以是一个变量。
[root@base shell]# name1=bdqn
[root@base shell]# name2=yjs
[root@base shell]# name3=$name1
[root@base shell]# echo $name3
Bdqn
那么问题来了,如果我们现在把name1的值改了,name3的值是否也会跟着改变那? 不会
[root@base shell]# name1=base
[root@base shell]# echo $name3
bdqn
命名法则举例:
studentname====>大驼峰StudentName,小驼峰:studentName(单驼
峰 双驼峰 三驼峰)
变量命名
shell中的变量必须以字母或者下划线开头,后面可以跟数字、字母和下划线,长度没有限制,区分大小写。 "见名知意"
变量赋值和取值
定义变量:变量名=变量值
注意点1:变量名和变量值之间用等号紧紧相连,之间没有任何空格
[root@base shell]# name=john
[root@base shell]# name= john
-bash: john: command not found
注意点2:当变量值中有空格时必须用引号括起,否则会出现错误,可以是双引号,也可以是单引号
[root@base shell]# name=li lei
-bash: lei: command not found
变量的取值很简单,在变量名前加$号就可以了,严谨的方法是${}。建议用后者
[root@base shell]# echo $name
li lei
[root@base shell]# echo ${name}
li lei
PS: 这里注意,在很多时候,运行脚本会提示错误,仔细检查脚本后还是不确定哪里出现了问题,那么这时候不妨将变量的取值写成标准格式,说不定就会有惊喜。
取消变量
取消变量使用unset,后面跟变量名。函数也是可以被取消的,unset后面也是可以跟上函数名来取消函数的。
[root@base shell]# unset name
[root@base shell]# echo ${name}
PS:当然一般在不用变量的时候,我们都会取消变量,当然退出进程也可以取消变量,但一般我们都会人为的再去取消一下用uset 命令
//这里注意一下我unset 的时候没有用$符号,一般引用的时候加$符号,也就是说前面的命令如果识别不出来变量的时候,就像我们用echo的时候,如果你不加$符号,他会把后面的当做字符串
set命令可以显示所有变量:包括局部变量和环境变量。也会显示一些
函数。env、printenv、export、declare -x也可以。
特殊变量
上边我们接触到局部变量和环境(全局)变量,也知道了他们特点:局部变量只在当前shell有效,环境变量的有效范围为当前shell和子shell。除了这些还有其他一些变量,也需要我们注意
1、只读变量(常量):
只能声明,但不能修改和删除
[root@base shell]# readonly name1="wukong"
[root@base shell]# echo $name1
wukong
[root@base shell]# name1=bajie
-bash: name1: readonly variable
还可以使用declare -r 声明常量。
[root@base shell]# declare -r name2="bajie"
[root@base shell]# echo $name2
bajie
readonly:有效期直到是进程结束。那么这个只读变量有什么用那?比方说 pi=3.14 3.1415926
//查看常量值
[root@base ~]# readonly -p
2、括号的使用
小括号用法:一次性使用。不会对环境产生影响
[root@base shell]# (name=yjs;echo $name)
yjs
[root@base shell]# echo $name
大括号:前后有空格。这个是对全局有影响的。
[root@base shell]# { name=adam; echo $name; }
adam
[root@base shell]# echo $name
adam
3、位置变量
shell中还有一些预先定义的特殊只读变量,它们的值只有在脚本运行时才能确定。
$0:代表脚本本身名字
$1----$9:第一个位置参数-------第9个位置参数
$#:脚本参数的个数总和
$@:表示脚本的所有参数
$* :表示脚本的所有参数
[root@base shell]# cat example.sh
#!/bin/bash
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
[root@base shell]# bash example.sh 1 2 3 4 5
这个脚本的名字是:example.sh
参数一共有:5个
参数的列表是: 1 2 3 4 5
参数的列表是: 1 2 3 4 5
第一个参数是: 1
第二个参数是: 2
第三个参数是:3
//如果有10个以上的数改怎么表示
[root@base shell]# echo {1..15}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[root@base shell]# echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
怎么才能让它识别为$10那?这个时候就要用到花括号了。
[root@base shell]# cat example.sh
#!/bin/bash
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
[root@base shell]# bash example.sh {1..15}
这个脚本的名字是:example.sh
参数一共有:15个
参数的列表是: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
参数的列表是: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
第一个参数是: 1
第二个参数是: 2
第三个参数是:3
第12个参数是:12
//那么具体这个有什么用那?,其实可以这样,假设我们要复制一个文件到另一台虚拟机。用什么命令那?scp、
[root@base shell]# cat /etc/redhat-release >
mylrelease.txt
[root@base shell]# cat mylrelease.txt
CentOS Linux release 7.6.1810 (Core)
//可以用脚本来写了
[root@base shell]# cat scp.sh
#!/bin/bash
echo "start copy..."
scp $1 root@192.168.1.61:/root/
echo "copy is finished"
[root@base shell]# bash scp.sh mylrelease.txt
start copy...
root@192.168.1.61's password:
mylrelease.txt 100%
38 22.6KB/s 00:00
copy is finished
//免密登录。传东西不用输入密码。
[root@base shell]# ssh-keygen -t ecdsa
Ssh-copy-id -i ~/.ssh/id_ecdsa.pub root@192.168.1.15
输入另一台虚拟机密码然后切换到另一台虚拟机
[root@base shell]# ssh-keygen -t rsa
//那么如果要传送多个文件,这个脚本改怎么写那?是不是把$1 改成$* 啊?那么用sed语句应该咋么写?
[root@base shell]# sed -i 's/$1/$*/' scp.sh
[root@base shell]# cat scp.sh
[root@base shell]# bash scp.sh /etc/fstab /tmp/*
$* 和 $@ 的区别。
$*和$@都代表了脚本的所有参数,但是,$*它会把显示的结果当做一个整体显示,而$@会把收集到的结果分开来显示。不过要注意,如果$*和$@有双引号的前提下,显示效果会做区分,如果都没有双引号,
效果一样。
//理论上,t2脚本的第一个参数,显示的并不正确
[root@base shell]# cat t1.sh
#!/bin/bash
echo "t1脚本所有参数是:$*"
bash t2.sh "$*"
[root@base shell]# cat t2.sh
#!/bin/bash
echo "t2脚本的第一个参数:$1"
[root@base shell]# bash t1.sh 1 2 3
t1脚本所有参数是:1 2 3
t2脚本的第一个参数:1 2 3
//把t1.sh中的 bash t2.sh “$*” 换成 $@ 用sed 怎么换。
[root@base shell]# sed -i '3s/\$\*/\$\@/' t1.sh
[root@base shell]# bash t1.sh 1 2 3
t1脚本所有参数是:1 2 3
t2脚本的第一个参数:1
$0 引用名称的使用
再来说这个$0,$0是名称,那么如果说,我把一个脚本,添加一个软连接。这个时候如果再运行脚本,那么它的$0显示的名字应该是原来的名字那?还是更改过后的名字?
[root@base shell]# ln -s example.sh link.sh
[root@base shell]# bash link.sh
这个脚本的名字是:link.sh
参数一共有:0个
参数的列表是:
....
那么以后我们就可以利用这一点,即便是同一个脚本,到时候也可以拓展出不同的功能。比如说,就可以利用这一点。那么就可以让他们返回的值是不一样的。这个就是以后在写脚本的时候,就要注意下,需要对$0做判断了。
shift命令: 整体往左移位
[root@base shell]# cat example.sh
#!/bin/bash
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
shift 2
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
shift 2
echo "这个脚本的名字是:$0"
echo "参数一共有:$#个"
echo "参数的列表是: $@"
echo "参数的列表是: $*"
echo "第一个参数是: $1"
echo "第二个参数是: $2"
echo "第三个参数是:$3"
echo "第12个参数是:${12}"
PS :当然这是默认的移动一位,当然也可以通过设置决定移动多少位,在 shift 后边直接加上想要移动的位数即可。
- 退出状态
进程使用退出状态来报告成功或者失败:
$? 变量保存最近的命令退出状态
0 代表成功,1~255 代表失败
举例:
[root@base shell]# ls /tmp/
[root@base shell]# echo $?
0
当然判断命令是否执行成功,不仅仅是简单的判断$?还有更严谨的判断语句,这个我们以后会讲,现在先知道就可以了。那么在这里还需要知道一 点。我们的脚本里边,往往是大于一条命令的,这个$?返回的0值,如果返回的是0,那么这个0值到底是返回的哪一条命令的值那?
[root@base shell]# cat test1.sh
#!/bin/bash
hostname
[root@base shell]# bash test1.sh
base
[root@base shell]# echo $?
0
PS : 那么我们就要考虑,根据不同的返回码,我们是不是就可判断出系统的运行状态啊?假设说,我们规定了计算机会有几种情况,如果说一种情况返回10,另一种情况返回20,还有30,那么我们就可根据系统返回的值的不同,来判断出,系统肯定是满足了某种条件。这样就可以作为定位判断系统状态的依据,其实在我们上网的时候有返回发404,其实也是类似这种方法的。200 OK 404 Not Found
3xx重定向 4xx客户端错误 5xx服务器错误
当然exit命令在外部也可以使用,意思是退出bash
数组
数组是一种特殊的数据结构,其中的每一项被称为一个元素,对于每个元素,都可以用索引的方式取出元素的值。使用数组的典型场景是一次性要记录很多类型相同的数据时(但不一定必须要相同)。比如,为了记录班级中所有人的数学成绩,如果不用数据来处理,那就只能定义所有人成绩的变量。shell中的数组对元素个数没有限制,但只支持一维数组。
- 定义数组
//使用declare命令定义数组
[root@base shell]# declare -a List
[root@base shell]# List[0]=Tom
[root@base shell]# List[1]=Jerry
[root@base shell]# List[2]=Dog
[root@base shell]# echo ${List[0]}
Tom
[root@base shell]# echo ${List[1]}
Jerry
[root@base shell]# echo ${List[2]}
Dog
//当然,也可以在创建数组的同时赋值,declare -a也可以省略不写
[root@base shell]# Name=("zhangsan" "li si" "wang wu")
[root@base shell]# echo ${Name[0]}
zhangsan
[root@base shell]# echo ${Name[1]}
li si
[root@base shell]# echo ${Name[2]}
wang wu
//也可以用负数,表示倒叙
[root@base shell]# echo ${Name[-1]}
wang wu
[root@base shell]# echo ${Name[-2]}
li si
[root@base shell]# echo ${Name[-3]}
Zhangsan
PS: 注意数组多个元素之间,不要用符号,否则会把多个元素当做整体一起输出。
[root@base shell]# name=("zhangsan","li si","wang wu")
[root@base shell]# echo $name
zhangsan,li si,wang wu
[root@base shell]# echo ${name[0]}
zhangsan,li si,wang wu
[root@base shell]# echo ${name[1]}
//还可以使用跳号赋值,也就是说可以指定索引号。
[root@base shell]# Score=([3]=3 [5]=5 [7]=7)
[root@base shell]# echo ${Score[3]}
3
[root@base shell]# echo ${Score[5]}
5
[root@base shell]# echo ${Score[7]}
7
- 数组的取值
数组取值:格式为:${数组名[索引]}
默认显示数字第一个元素值
[root@base shell]# echo ${Name[0]}
zhangsan
[root@base shell]# echo ${Name}
Zhangsan
取出对应索引下标的值
[root@base shell]# echo ${Name[2]}
wang wu
[root@base shell]# echo ${Name[1]}
li si
[root@base shell]# echo ${Name[0]}
Zhangsan
PS: 注意的是,数组内索引为0 的才是第一个。
- 一次取出数组所有的值
@ //得到的是以空格隔开的元素
[root@base shell]# echo ${Name[@]}
zhangsan li si wang wu
* //得到的是一整个字符串
[root@base shell]# echo ${Name[*]}
zhangsan li si wang wu
- 数组长度: 即数组元素个数
利用“@”或“*”字符,可以将数组扩展成列表,然后使用“#”来获取数组元素的个数。
[root@base shell]# echo ${#Name[*]}
3
[root@base shell]# echo ${#Name[@]}
3
- 数组的截取(分片)(切片)
可以截取某个元素的一部分,对象可以是整个数组或某个元素。(数组的切片:右不包括行为)
//定义数组
[root@base shell]# Number=(1 2 3 4 5 6 7 8)
[root@base shell]# echo ${Number[*]}
1 2 3 4 5 6 7 8
[root@base shell]# echo ${#Number[*]}
8
//取出第一个到第3个元素的值
[root@base shell]# echo ${Number[@]:0:3}
1 2 3
//取出第3个到第6个元素的值
[root@base shell]# echo ${Number[@]:2:4}
3 4 5 6
PS: 到这里,同学们可能就会有点懵了,需要大家注意的是,数组分片的书写格式的意义表示:从第几个开始连续出现几个字符。
//也可以取出某个元素的第几个开始到第n个字符
[root@base shell]# Word=(hello world and welcome)
[root@base shell]# echo ${Word[2]:0:3}
and
[root@base shell]# echo ${Word[1]:0:3}
wor
[root@base shell]# echo ${Word[1]:0:4}
Worl
- 连接数组
将若干个数组进行拼接操作
[root@base shell]# echo ${Number[@]}
1 2 3 4 5 6 7 8
[root@base shell]# echo ${Word[@]}
hello world and welcome
//将两个数组的元素全部连接
[root@base shell]# NewNum=(${Number[@]} ${Word[@]})
[root@base shell]# echo $NewNum
1
[root@base shell]# echo ${NewNum[@]}
1 2 3 4 5 6 7 8 hello world and welcome
//那么现在我们重组新的数组,截取Word数组的第2个元素从第0个连续5个字符,和Number数组里的第2个字符开始连续3个字符。
[root@base shell]# New=(${Word[2]:0:5} ${Number[@]:1:3})
[root@base shell]# echo ${New[@]}
and 2 3 4
- 替换元素
将数组内某个元素的值替换成其他值。如果匹配不到则没有改动
[root@base shell]# Name=(zhangsan lisi wangwu zhaoliu)
[root@base shell]# echo ${Name[@]}
[root@base shell]# echo ${Name[@]}
zhangsan lisi wangwu zhaoliu
//将lisi替换为lilei
[root@base shell]# Name=(${Name[@]/lisi/lilei})
[root@base shell]# echo ${Name[@]}
zhangsan lilei wangwu zhaoliu
//将zhaoliu替换为zhaosi
[root@base shell]# Name=(${Name[@]/zhaoliu/zhaosi})
[root@base shell]# echo ${Name[@]}
zhangsan lilei wangwu zhaosi
//上边的写法可以达到要求,但较为复杂,其实可以完全按照“变量” 重新赋值的方式,直接写数组的对应的元素的值。比如,上述例子中将zhangsan改为 wukong,就可以简化为
[root@base ~]# Name[0]=wukong
[root@base ~]# echo ${Name[@]}
wukong lilei wangwu zhaosi
//取消数组或元素
使用unset命令,取消数组中的一个元素
[root@base shell]# unset Name[2]
[root@base shell]# echo ${Name[@]}
zhangsan lilei zhaosi
//取消整个数组,=取消变量全部
[root@base shell]# unset Name
[root@base shell]# echo ${Name[@]}
数组练习
1、设置两个变量组,groupA和groupB.groupA的成员包括:zhangsan 、lisi、wangwu、zhaoliu
groupB的成员包括:gangtiexia、zhizhuxia、heiguafu、lvjuren、
meidui
[root@shell bin]# groupA=(zhangsan lisi wangsu zhaoliu)
[root@shell bin]# groupB=(gangtiexia zhizhuxia heiguafu
lvjuren meidui)
2、显示每个组的全部成员:
[root@shell bin]# echo ${groupA[@]}
zhangsan lisi wangsu zhaoliu
[root@shell bin]# echo ${groupB[@]}
gangtiexia zhizhuxia heiguafu lvjuren meidui
3、查看groupA的第2个成员。并查看groupB的第3个成员。
[root@shell bin]# echo ${groupA[1]}
lisi
[root@shell bin]# echo ${groupB[2]}
heiguafu
4.将groupA的第4个成员替换为zhaosi,groupB的第一个成员替换为 heibao.
[root@shell bin]# echo ${groupA[@]}
zhangsan lisi wangsu zhaoliu
[root@shell bin]# groupA[3]=zhaosi
[root@shell bin]# echo ${groupA[@]}
zhangsan lisi wangsu zhaosi
[root@shell bin]# groupB[0]=heibao
[root@shell bin]# echo ${groupB[@]}
heibao zhizhuxia heiguafu lvjuren meidui
- 抽取groupA中的成员,和groupB中成员,组成groupC
[root@shell bin]# groupC=(${groupA[@]} ${groupB[@]})
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi wangsu zhaosi heibao zhizhuxia heiguafu
lvjuren meidui
6、显示groupC的全部成员,并且将第8个成员替换为liuneng
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi wangsu zhaosi heibao zhizhuxia heiguafu
lvjuren meidui
[root@shell bin]# groupC[7]=liuneng
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi wangsu zhaosi heibao zhizhuxia heiguafu
liuneng meidui
7、显示groupC的倒数第二个成员。
[root@shell bin]# echo ${groupC[-2]}
Liuneng
8、显示groupC中第二个元素开始到第五个元素。
[root@shell bin]# echo ${groupC[@]:1:4}
lisi zhaosi heibao zhizhuxia
[root@shell bin]# echo ${groupC[@]}
zhangsan lisi zhaosi heibao zhizhuxia heiguafu liuneng
meidui
转义和引用
shell中有很多特殊字符,会有特殊意义,但是有时候会造成麻烦,需要转义才可以使用,转义符号为“\”
[root@base shell]# a=$Dollar
[root@base shell]# echo $a
[root@base shell]# a=\$Dollar
[root@base shell]# echo $a
$Dollar
引用是指将字符串用某种符号括起来,以防止特殊字符被解析为其他意思。shell中一共有4种引用符,分别为双引号、单引号、反引号和转义符。
双引号可以引用除$符号、反引号、转义符之外的所有字符;单引号可以引用所有字符;反引号则会将反引号中的内容解释为系统命令。
root@base shell]# who am i
root pts/0 2020-05-12 00:03 (192.168.1.1)
[root@base shell]# echo "who am i"
who am i
[root@base shell]# echo 'who am i'
who am i
[root@base shell]# echo `who am i`
root pts/0 2020-05-12 00:03 (192.168.1.1)
[root@base shell]# echo $PWD
/opt/shell
[root@base shell]# echo "$PWD"
/opt/shell
[root@base shell]# echo '$PWD'
$PWD
[root@base shell]# echo `$PWD`
bash: /opt/shell: Is a directory
运算符
shell中的运算符主要有比较运算符(用于整数比较)、字符串运算符(用于字符串测试)、文件操作运算符(用于文件测试)、逻辑运算符、算术 运算符、位运算符、自增自减运算符等。
1、算术运算符
算术运算符指的是加、减、乘、除、余、幂等常见的算术运算,以及加等、减等、乘等、除等、余等复合算术运算。要特别注意的是,shell只支 持整数计算,也就是说所有可能产生小数的运算都会舍去小数部分。
/ 除法
% 取模-返回除法的余数
余数是整数除法中被除数未被除尽部分,余数的取值范围为0-除数(不包括除数)的整数
eg:27%6 商数为4,余数为3
若被除数小于除数,则商为0,余数为被除数本身。
Bash shell 的算术运算有四种方式:
1:使用 expr 外部程式
[root@shell ~]# a=`expr 4 + 5`
[root@shell ~]# echo $a
9
PS:注意! '4' '+' '5' 这三者之间要有空白
[root@shell ~]# a=`expr 4+5`
[root@shell ~]# echo $a
4+5
[root@shell ~]# a=`expr 4 * 5`
expr: 语法错误
[root@shell ~]# a=`expr 4 \* 5`
[root@shell ~]# echo $a
20
2:使用 $(( ))
[root@shell ~]# a=$((4 + 5))
[root@shell ~]# echo $a
9
3:使用 $[ ]
[root@shell ~]# a=$[4 + 5]
[root@shell ~]# echo $a
9
4:使用let 命令
[root@shell ~]# m=2
[root@shell ~]# n=3
[root@shell ~]# let a=m+n
[root@shell ~]# echo $a
5: 总结
总结:4种写法的算术运算,例子以数字2、3举例
PS: let命令可以直接用变量名,并且支持变量名和具体数值之间的各种运算。
[root@shell ~]# cat yunsuan.sh
#!/bin/bash
jia=`expr $1 + $2`
echo "参数相加 : $jia"
jian=`expr $1 - $2`
echo "参数相减 :$jian"
cheng=`expr $1 \* $2`
echo "参数相乘 :$cheng"
chu=`expr $1 / $2`
echo "参数相除:$chu"
qumo=`expr $1 % $2`
echo "参数取模:$qumo"
if [ $1 == $2 ]
then
echo "两个参数相等"
fi
if [ $1 != $2 ]
then
echo "两个参数不相等"
fi
PS: 乘号(*)前边必须加反斜杠()才能实现乘法运算;
if...then...fi 是条件语句,后续将会讲解。
2、与运算、或运算、非运算
在说这个之前,先来说一下10进制转2进制。
将一个十进制数除以二,得到的商再除以二,依此类推直到商等于一或零时为止,倒取除得的余数,即换算为二进制数的结果。只需记住要点:除二取余,倒序排列。
二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个乘以2 的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。
与运算:&(并且)and
0 false 假
1 true 真
0&0=0
0&1=0
1&0=0
1&1=1
或运算:| 或者 or
0|0=0
0|1=1
1|0=1
1|1=1
非运算:^
(一山不容二虎,除非一公一母。。。)
0^1=1
0^0=0
1^0=1
1^1=0
[root@shell ~]# let "c=100&125"
[root@shell ~]# echo $c
100
[root@shell ~]# let "c=100|125"
[root@shell ~]# echo $c
125
[root@shell ~]# let "c=100^125"
[root@shell ~]# echo $c
25
短路与 && 逻辑与
0&&0=0
0&&1=0
1&&0=0
1&&1=1
如果是命令:
cmd1 && cmd2
如果cmd1为假, cmd2不需要执行。反之cmd1为真,需要执行cmd2
拿到命令里来说: 前边执行成功,后边的才会执行
短路或 || 逻辑或
0||0=0
0||1=1
1||0=1
1||1=1
cmd1 || cmd2
如果cmd1为真,cmd2不需要执行。反之cmd1为假,需要执行cmd2.
那么这个时候就来想一下,如果我想要添加用户,想要实现,如果没有此用户,我就添加,应该怎么来实现。
[root@shell ~]# id user1 && useradd user1
id: user1: no such user
[root@shell ~]# id user1 && useradd user1
id: user1: no such user
[root@shell ~]# id user1 || useradd user1
id: user1: no such user
[root@shell ~]# id user1 || useradd user1
uid=1000(user1) gid=1000(user1) groups=1000(user1)
那么再来想一下,修改用户user2的密码,如果有用户直接修改,如果没有先添加用户
[root@base ~]# echo 123.com | passwd --stdin user2 ||
useradd user2
passwd: Unknown user name 'user2'.
[root@base ~]# echo 123.com | passwd --stdin user2 ||
useradd user2
Changing password for user user2.
passwd: all authentication tokens updated successfully.
练习:
定义两个数组groupA和 groupB,groupA中的元素分别为:zhangsan lisi wangwu zhaoliu,
groupB中的元素分别为:gangtiexia zhizhuxia heiguafu lvjuren
meidui。写出一个脚本。
判断两个参数是否相等,如果相等则打印出“元素数量相等”,如果不相等则打印“元素数量不相等”
groupA\groupB\元素的数量相加等于多少
groupA\groupB\元素的数量相减等于多少
groupA\groupB\元素的数量相乘等于多少
groupA\groupB\元素的数量相除等于多少
groupA\groupB\元素的数量取模是多少
groupA\groupB\元素的数量等于多少
[root@base ~]# cat jiaoben1.sh
#!/bin/bash
if [ $1 == $2 ]
then
echo "两个参数相等"
fi
if [ $1 != $2 ]
then
echo "两个参数不相等"
fi
xiangjia=`expr $1 + $2`
echo "参数相加:$xiangjia"
xiangjian=`expr $1 - $2`
echo "参数相减:$xiangjian"
xiangcheng=`expr $1 \* $2`
echo "参数想乘:$xiangcheng"
xiangchu=`expr $1 / $2`
echo "参数相除:$xiangchu"
qumo=`expr $1 % $2`
echo "参数取模:$qumo"
[root@base ~]# groupA=(zhangsan lisi wangwu zhaoliu)
[root@base ~]# groupB=(gangtiexia zhizhuxia heiguafu
lvjuren meidui)
[root@base ~]# a=`echo ${#groupA[@]}`
[root@base ~]# b=`echo ${#groupB[@]}`
[root@base ~]# bash jiaoben1.sh $a $b
两个参数不相等
参数相加:9
参数相减:-1
参数想乘:20
参数相除:0
参数取模:4
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
-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。
PS: eq: equql平等的,相等的
gt: greater较大的,大于
lt: less then 小于
举例脚本:
#!/bin/bash
if [ $1 -eq $2 ]
then
echo "$1 -eq $2 : $1 等于 $2"
else
echo "$1 -eq $2 : $1 不等于 $2"
fi
if [ $1 -ne $2 ]
then
echo "$1 -ne $2 : $1 不等于 $2"
else
echo "$1 -ne $2 : $1 等于 $2"
fi
if [ $1 -gt $2 ]
then
echo "$1 -gt $2 : $1 大于 $2"
else
echo "$1 -gt $2 : $1 不大于 $2"
fi
if [ $1 -lt $2 ]
then
echo "$1 -lt $2 : $1 小于 $2"
else
echo "$1 -lt $2 : $1 不小于 $2"
fi
if [ $1 -ge $2 ]
then
echo "$1 -ge $2 : $1 大于或等于 $2"
else
echo "$1 -ge $2 : $1 小于 $2"
fi
if [ $1 -le $2 ]
then
echo "$1 -le $2 : $1 小于或等于 $2"
else
echo "$1 -le $2 : $1 大于 $2"
fi
练习写一个脚本:
判断两个参数的大小,要求:
如果参数1大于参数2,那么两数相减,并输出结果值“因为参数1大于参数
2,所以两数相减,结果值为:”
如果参数1等于参数2,那么两数想乘,并输出结果值“因为参数1等于参数
2,所以两数相乘,结果值为:”
如果参数1小于参数2,那么两数想加,并输出结果值“因为参数1小于参数
2,所以两数相加,结果值为:”
#!/bin/bash
if [ $1 -gt $2 ]
then
echo "参数$1 大于 $2 ,所以两数相减等于:`expr $1 - $2`"
fi
if [ $1 -eq $2 ]
then
echo "参数$1 等于 $2,所以两数相乘等于:`expr $1 \* $2`"
fi
if [ $1 -lt $2 ]
then
echo "参数$1 小于 $2 ,所以两数相加等于:`expr $1 + $2`"
fi