shell脚本1

什么是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

运行脚本有两种方法:

  1. 使用bash命令执行

[root@base shell]# bash Helloworld.sh

Hello world

// “.”命令也都可以执行脚本,且不需要可执行权限

[root@base shell]# . Helloworld.sh

Hello world

  1. 给脚本添加可执行权限,然后直接就可以执行了

[root@base shell]# chmod +x Helloworld.sh

[root@base shell]# ./Helloworld.sh

Hello world

PS: 脚本是各种命令的集合,终究还是要执行服务的。

  1. 查看命令存储路径

[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 后边直接加上想要移动的位数即可。

  1. 退出状态

进程使用退出状态来报告成功或者失败:

$? 变量保存最近的命令退出状态

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中的数组对元素个数没有限制,但只支持一维数组。

  1. 定义数组

//使用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

  1. 数组的取值

数组取值:格式为:${数组名[索引]}

默认显示数字第一个元素值

[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 的才是第一个。

  1. 一次取出数组所有的值

@ //得到的是以空格隔开的元素

[root@base shell]# echo ${Name[@]}

zhangsan li si wang wu

* //得到的是一整个字符串

[root@base shell]# echo ${Name[*]}

zhangsan li si wang wu

  1. 数组长度: 即数组元素个数

利用“@”或“*”字符,可以将数组扩展成列表,然后使用“#”来获取数组元素的个数。

[root@base shell]# echo ${#Name[*]}

3

[root@base shell]# echo ${#Name[@]}

3

  1. 数组的截取(分片)(切片)

可以截取某个元素的一部分,对象可以是整个数组或某个元素。(数组的切片:右不包括行为)

//定义数组

[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

  1. 连接数组

将若干个数组进行拼接操作

[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

  1. 替换元素

将数组内某个元素的值替换成其他值。如果匹配不到则没有改动

[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

  1. 抽取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举例

加法

减法

乘法

除法

取模

幂运算

expr

命令:

`expr 2 + 3`

`expr

2 - 3`

`expr 2 * 3`

`expr

2 / 3`

`expr

2 % 3`

No

使用

$(())

$((2 +

3))

$((2 -

3))

$((2 *

3))

$((2 /

3))

$((2 %

3))

$((2 **

3))

使用

$[]:

$[2 +

3]

$[2 -

3]

$[2 *

3]

$[2 /

3]

$[2 %

3]

$[2 **

3]

let命令:

let

a=m+n

let

a=mn

let

a=m*n

let

a=m/n

let

a=m%n

let

a=m**n

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

运行脚本有两种方法:

  1. 使用bash命令执行

[root@base shell]# bash Helloworld.sh

Hello world

// “.”命令也都可以执行脚本,且不需要可执行权限

[root@base shell]# . Helloworld.sh

Hello world

  1. 给脚本添加可执行权限,然后直接就可以执行了

[root@base shell]# chmod +x Helloworld.sh

[root@base shell]# ./Helloworld.sh

Hello world

PS: 脚本是各种命令的集合,终究还是要执行服务的。

  1. 查看命令存储路径

[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 后边直接加上想要移动的位数即可。

  1. 退出状态

进程使用退出状态来报告成功或者失败:

$? 变量保存最近的命令退出状态

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中的数组对元素个数没有限制,但只支持一维数组。

  1. 定义数组

//使用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

  1. 数组的取值

数组取值:格式为:${数组名[索引]}

默认显示数字第一个元素值

[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 的才是第一个。

  1. 一次取出数组所有的值

@ //得到的是以空格隔开的元素

[root@base shell]# echo ${Name[@]}

zhangsan li si wang wu

* //得到的是一整个字符串

[root@base shell]# echo ${Name[*]}

zhangsan li si wang wu

  1. 数组长度: 即数组元素个数

利用“@”或“*”字符,可以将数组扩展成列表,然后使用“#”来获取数组元素的个数。

[root@base shell]# echo ${#Name[*]}

3

[root@base shell]# echo ${#Name[@]}

3

  1. 数组的截取(分片)(切片)

可以截取某个元素的一部分,对象可以是整个数组或某个元素。(数组的切片:右不包括行为)

//定义数组

[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

  1. 连接数组

将若干个数组进行拼接操作

[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

  1. 替换元素

将数组内某个元素的值替换成其他值。如果匹配不到则没有改动

[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

  1. 抽取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举例

加法

减法

乘法

除法

取模

幂运算

expr

命令:

`expr 2 + 3`

`expr

2 - 3`

`expr 2 * 3`

`expr

2 / 3`

`expr

2 % 3`

No

使用

$(())

$((2 +

3))

$((2 -

3))

$((2 *

3))

$((2 /

3))

$((2 %

3))

$((2 **

3))

使用

$[]:

$[2 +

3]

$[2 -

3]

$[2 *

3]

$[2 /

3]

$[2 %

3]

$[2 **

3]

let命令:

let

a=m+n

let

a=mn

let

a=m*n

let

a=m/n

let

a=m%n

let

a=m**n

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sandman"

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值