一、shell介绍🍒
shell是一个程序,采用C语言编写,是用户和linux内核沟通的桥梁。它既是一种命令语言,又是一种解释性的编程语言。通过一个图表来查看一下shell的作用。
二、变量🍋
1、变量介绍
变量
:变量是编程中最常用的一种临时在内存中存取数据的一种方式。
变量存取原理
关于内存的说明
a、系统启动 -> 内存被按照1B一个单位划分成N块 -> 并且以十六进制为每一个空间编号b、内存跟踪表记录 -> 使用和未使用的内存的地址编号
c、内存申请 -> 系统从未使用的内存中拿出一个或者一段连续空间给你使用,同时在内存跟踪表中记录该地址被占用不在分给别人,同时在系统中建立映射机制 示例:变量名 STRING1='ABC' name<==>0x5
d、释放内存 从内存跟踪表中将记录删除,下次存数据直接覆盖
2、变量定义
2.1 什么时候需要定义变量?
如果某个内容需要多次使用,并且在代码中重复出现,那么可以用变量代表该内容。这样在修改内容的时候,仅仅需要修改变量的值。在代码运作的过程中,可能会把某些命令的执行结果保存起来,后续代码需要使用这些结果,就可以直接使用这个变量。
2.2 定义一个变量
在shell编程中的变量名和等号之间不能有空格。
变量名命名规则:
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
中间不能有空格,可以使用下划线(_)。
不能使用标点符号。
字符串要用单引号或双引号引起来
不能使用bash里的关键字(可用help命令查看保留关键字)。
定义变量演示:
变量赋值,此种方法设置为本地变量
[root@zutuanxue ~]# name="baism"
[root@zutuanxue ~]# school='ayitula'
[root@zutuanxue ~]# age=30
[root@zutuanxue ~]# score=88.8
2.3 取消变量 unset
取消当前环境中的变量,如果是变量设置是保存在文件中,下次重启又会恢复
[root@zutuanxue ~]# unset name
[root@zutuanxue ~]# echo $name
2.4 有类型变量 declare
-i 将变量看成整数
-r 使变量只读 readonly,该变量的值无法改变,并且不能为unset
-x 标记变量通过环境导出 export
-a 指定为索引数组(普通数组);查看普通数组
-A 指定为关联数组;查看关联数组
[root@zutuanxue ~]# declare -i num='asa'
[root@zutuanxue ~]# echo $num
0
[root@zutuanxue ~]# num=100
[root@zutuanxue ~]# echo $num
100
[root@zutuanxue ~]# declare -r num
[root@zutuanxue ~]# echo $num
100
[root@zutuanxue~]# num=200
-bash: num: 只读变量
[root@zutuanxue ~]# declare -x
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="Bai_Shuming"
declare -x LANG="zh_CN.UTF-8"
declare -x LESSOPEN="||/usr/bin/lesspipe.sh %s"
3、变量分类
系统中的变量根据作用域及生命周期可以分为四类:本地变量
、环境变量
、全局变量
、内置变量
3.1 本地变量
用户自定义的变量,定义在脚本或者当前终端中,脚本执行完毕或终端结束变量失效。
3.2 环境变量
定义在用户家目录下的.bashrc或.bash_profile文件中,用户私有变量,只能本用户使用。
查看当前用户的环境变量 env
查询当前用户的所有变量(临时变量与环境变量) set
3.3 将当前变量变成环境变量 export
# 示例一
[root@zutuanxue tmp]# export A=hello //临时将一个本地变量(临时变量)变成环境变量
[root@zutuanxue tmp]# env|grep ^A
A=hello
# 示例二
[root@zutuanxue tmp]# A=HELLO
[root@zutuanxue tmp]# export A
# 示例三:定义一个永久生效变量:
vim .bash_profile 或者 ~/.bashrc
A=hello
关于export说明
用户登录时:
用户登录到Linux系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行 shell脚本程序。
运行脚本时:
运行shell脚本程序时,系统将创建一个子shell。此时,系统中将有两个shell,一个是登录时系统启动的shell,另一个是系统为运行脚本程序创建的shell。当一个脚本程序运行完毕,它的脚本shell将终止,可以返回到执行该脚本之前的shell。
从这种意义上来说,用户可以有许多 shell,每个shell都是由某个shell(称为父shell)派生的。在子shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内的一个局部变量,其他的shell不能引用它,要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。
export命令将使系统在创建每一个新的shell时定义这个变量的一个拷贝。这个过程称之为变量输出。
父shell与子shell
3.4 全局变量
使用export命令将本地变量输出为当前shell中的环境变量
所有用户及shell都可以使用,可以在/etc/profile /etc/bashrc下永久定义
打印全局变量 printenv
定义格式
export SCHOOL='zutuanxue'
测试方法:
通过不同用户登录测试是否能读取变量
3.5 内置变量
系统变量(内置bash中变量) : shell本身已经固定好了它的名字和作用.
(1)$?
:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
若退出状态值为0,表示命令运行成功
若退出状态值为127,表示command not found
若退出状态值为126,表示找到了该命令但无法执行(权限不够)
若退出状态值为1&2,表示没有那个文件或目录
(2)$$
:当前所在进程的进程号
eg:# echo $$ eg:kill -9 echo $$
= exit 退出当前会话
(3)$!
:后台运行的最后一个进程号 (当前终端)
eg: # gedit &
(4)!$
: 调用最后一条命令历史中的参数
(5)!!
: 调用最后一条命令历史
(6)$#
:脚本后面接的参数的个数
(7)$*
:脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开
(8)$@
: 脚本后面所有参数,参数是独立的,也是全部输出
了解$*和$@的区别:
$* :表示将变量看成一个整体
$@ :表示变量是独立的
#!/bin/bash
for i in "$@"
do
echo $i
done
echo "======我是分割线======="
for i in "$*"
do
echo $i
done
[root@zutuanxue shell01]$ bash 3.sh a b c
a
b
c
======我是分割线=======
a b c
(8)$0
:当前执行的进程/程序名
$1~$9 位置参数变量
${10}~${n} 扩展位置参数变量 第10个位置变量必须用{}大括号括起来
./1.sh a b c
变量总结说明:
本地变量:当前用户自定义的变量。当前进程中有效,其他进程及当前进程的子进程无效。
环境变量:当前进程有效,并且能够被子进程调用。
全局变量:全局所有的用户和程序都能调用,且继承,新建的用户也默认能调用.
内置变量:shell本身已经固定好了它的名字和作用.
变量类型 | 作用域 | 生命周期 |
---|---|---|
本地变量 | 当前shell环境(子shell不能用) | 脚本结束或终端结束 |
环境变量 | 当前shell或者子shell | 当前进程结束 |
全局变量 | 所有用户及shell环境 | 关机 |
内置变量 | 所有用户及shell环境 | 关机 |
4、变量取值
读取变量内容符: 读取方法:变量名
变量内容读出
[root@localhost ~]# echo $name
baism
[root@localhost ~]# echo $school
ayitula
[root@localhost ~]# echo $age
30
[root@localhost ~]# echo $score
88.8
注意
变量读取过程中,默认单引号是不解释变量的.比如
[root@localhost ~]# echo '$name'
$name
如果必须使用单引号还要读取变量的值可以使用eval命令[重新运算求出参数的内容]
[root@localhost ~]# eval echo '$name'
baism
5、其他变量(扩展)
1)取出一个目录下的目录和文件:dirname和 basename
2)变量"内容"的删除和替换
一个“%”代表从右往左去掉一个/key/
两个“%%”代表从右往左最大去掉/key/
一个“#”代表从左往右去掉一个/key/
两个“##”代表从左往右最大去掉/key/
[root@localhost ~]# dirname /etc/test/hosts
/etc/test
[root@localhost ~]# basename /etc/test/hosts
hosts
[root@localhost ~]# url=www.taobao.com
[root@localhost ~]# echo ${#url}
14
[root@localhost ~]# echo ${url#*.}
taobao.com
[root@localhost ~]# echo ${url##*.}
com
[root@localhost ~]# echo ${url%.*}
www.taobao
[root@localhost ~]# echo ${url%%.*}
www
三、shell格式化输出🫒
1、echo命令
功能:将内容输出到默认显示设备
应用场景:需要计算机程序输出的地方
echo命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。 功能说明:显示文字。
语法:echo [-ne][字符串]
补充说明:
1、echo会将输入的字符串送往标准输出。
2、输出的字符串间以空白字符隔开,并在最后加上换行号。
OPTIONS:
-n 不要在最后自动换行
-e 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
转义字符
\a 发出警告声;
\b 删除前一个字符;
\t 插入tab;
\n 换行且光标移至行首;
\c 最后不加上换行符号;
\f 换行但光标仍旧停留在原来的位置;
\r 光标移至行首,但不换行;
\v 与\f相同;
\ 插入\字符;
\0nnn 打印nnn(八进制)所代表的ASCII字符; 备注:数字0 不要理解成字母o
\xNN 打印NN(十六进制)所代表的ASCII字符;
-–help 显示帮助
-–version显示版本信息
2、输出颜色字体
脚本中echo显示内容带颜色显示,echo显示带颜色,需要使用参数-e
格式如下:
echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"
eg: echo -e “\033[41;36m something here \033[0m”
其中41的位置代表底色, 36m的位置是代表字的颜色
1、字背景颜色和文字颜色之间是英文的
2、文字颜色后面有个m
3、字符串前后可以没有空格,如果有的话,输出也是同样有空格
字颜色:30—–37
echo -e “\033[30m 黑色字 \033[0m”
echo -e “\033[31m 红色字 \033[0m”
echo -e “\033[32m 绿色字 \033[0m”
echo -e “\033[33m 黄色字 \033[0m”
echo -e “\033[34m 蓝色字 \033[0m”
echo -e “\033[35m 紫色字 \033[0m”
echo -e “\033[36m 天蓝字 \033[0m”
echo -e “\033[37m 白色字 \033[0m”
字背景颜色范围:40—–47
echo -e “\033[40;37m 黑底白字 \033[0m”
echo -e “\033[41;37m 红底白字 \033[0m”
echo -e “\033[42;37m 绿底白字 \033[0m”
echo -e “\033[43;37m 黄底白字 \033[0m”
echo -e “\033[44;37m 蓝底白字 \033[0m”
echo -e “\033[45;37m 紫底白字 \033[0m”
echo -e “\033[46;37m 天蓝底白字 \033[0m”
echo -e “\033[47;30m 白底黑字 \033[0m”
最后面控制选项说明
\033[0m 关闭所有属性
\033[1m 设置高亮度
\033[4m 下划线
\033[5m 闪烁
\033[7m 反显
\033[8m 消隐
\033[30m — \33[37m
设置前景色
\033[40m — \33[47m 设置背景色
\033[nA 光标上移n行
\033[nB 光标下移n行
\033[nC 光标右移n行
\033[nD 光标左移n行
\033[y;xH设置光标位置
\033[2J 清屏
\033[K 清除从光标到行尾的内容
\33[s 保存光标位置
\033[u 恢复光标位置
\033[?25l 隐藏光标
\033[?25h 显示光标
用法例子 光标下移三行
[root@localhost ~]# echo -e "\033[0m today is fine \033[3B"
today is fine
四、算术运算🍅
1、运算符与命令
四则运算符: + - * \ 【加减乘除】
扩展: % ** 【取余 开方】
运算命令:
- 整形运算
– expr
– let
– $(())
– bc - 浮点运算
– bc
2、整形运算
2.1 expr
命令
只能做整数运算,格式比较古板,注意空格
[root@localhost ~]# expr 1 + 1
2
[root@localhost ~]# expr 5 - 2
3
[root@localhost ~]# expr 5 \* 2 #注意*出现应该转义,否则认为是通配符
10
[root@localhost ~]# expr 5 / 2
2
[root@localhost ~]# expr 5 % 2
1
2.2 let
命令
只能做整数运算,且运算元素必须是变量,无法直接对整数做运算
[root@localhost ~]# let a=100+3;echo $a
103
[root@localhost ~]# let a=100-3;echo $a
97
[root@localhost ~]# let a=100/3;echo $a
33
[root@localhost ~]# let a=100*3;echo $a
300
[root@localhost ~]# let a=100%3;echo $a
1
[root@localhost ~]# let a=100**3;echo $a
1000000
[root@localhost ~]# a=100
[root@localhost ~]# let a++;echo $a
101
[root@localhost ~]# let a--;echo $a
100
[root@localhost ~]# let a-=3;echo $a
97
[root@localhost ~]# let a+=5;echo $a
102
2.3 (())
双小圆括号运算,在shell中(( ))也可以用来做数学运算
[root@localhost ~]# echo $(( 100+3))
103
[root@localhost ~]# echo $(( 100-3))
97
[root@localhost ~]# echo $(( 100%3))
1
[root@localhost ~]# echo $(( 100*3))
300
[root@localhost ~]# echo $(( 100/3))
33
[root@localhost ~]# echo $(( 100**3)) #开方运算
1000000
3、浮点运算
浮点运算是采用的命令组合的方式来实现的 echo “scale=N;表达式”|bc
[root@localhost ~]# echo "scale=2;3+100"|bc
103
[root@localhost ~]# echo "scale=2;100-3"|bc
97
[root@localhost ~]# echo "scale=2;100/3"|bc
33.33
[root@localhost ~]# echo "scale=2;100*3"|bc
300
注:linux下的bc命令可以通过scale参数设置结果的位数,但是scale只对除法、取余、乘幂有效,比如乘法就无效,想了一个回避的方法,就是除以1。
五、数组🥕
1、数组介绍
数组可以让用户一次赋予多个值,需要读取数据时只需通过索引调用就可以方便读出了。
普通数组:只能使用整数作为数组索引(元素的索引)
关联数组:可以使用字符串作为数组索引(元素的索引)
2、数组定义
数组名称=(元素1 元素2 元素3 ...)
3、数组赋值方式
- 一次附一个值
变量名=变量值
array[0]=v1
array[1]=v2
array[3]=v3
- 一次附多个值
array=(var1 var2 var3 var4)
array1=(`cat /etc/passwd`) //将文件中每一行赋值给array1数组
array2=(`ls /root`)
array3=(harry amy jack "Miss zhang")
array4=(1 2 3 4 "hello world" [10]=linux)
4、数组取值
取值方式: ${数组名称[索引]}
索引: 默认情况下索引是指数组中的元素[存的值]在数组中的顺序,从0开始计数,关联数组除外。比如:
- array=(var1 var2 var3 var4)
array数组中存有4个元素,分别是:var1 var2 var3 var4 ,那么我想取出var2这个元素,那么就得先看看他在数组中的位置,数组中的元素索引如下:
元素 var1 var2 var3 var4
索引 0 1 2 3
所以正确表示array数组中元素var2的方式是:${array[1]}
数组取值练习
${array[i]} i表示元素的索引
使用@ 或 * 可以获取数组中的所有元素:
echo ${array[0]} 获取第一个元素
echo ${array[*]} 获取数组里的所有元素
echo ${#array[*]} 获取数组里所有元素个数
echo ${!array[@]} 获取数组元素的索引索引
echo ${array[@]:1:2} 访问指定的元素;1代表从索引为1的元素开始获取;2代表获取后面几个元素
5、关联数组
5.1 定义关联数组
关联数组使用首先需要申明该数组为关联数组,申明方式: declare -A 数组名称
首先声明关联数组
declare -A asso_array1
declare -A asso_array2
declare -A asso_array3
5.2 关联数组赋值
- 一次赋一个值
数组名[索引]=变量值
[root@zutuanxue ~]# asso_array1[linux]=one
[root@zutuanxue ~]# asso_array1[java]=two
[root@zutuanxue ~]# asso_array1[php]=three
- 一次附多个值
[root@zutuanxue ~]# asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss zhang")
- 查看关联数组
[root@zutuanxue ~]# declare -A
declare -A asso_array1='([php]="three" [java]="two" [linux]="one" )'
declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss zhang" )'
5.3 管理数组取值
[root@localhost ~]# echo ${asso_array1[linux]}
one
[root@localhost ~]# echo ${asso_array1[php]}
three
[root@localhost ~]# echo ${asso_array1[*]}
three two one
[root@localhost ~]# echo ${!asso_array1[*]}
php java linux
[root@localhost ~]# echo ${#asso_array1[*]}
3
[root@localhost ~]# echo ${#asso_array2[*]}
4
[root@localhost ~]# echo ${!asso_array2[*]}
name3 name2 name1 name4
六、函数🌽
1、函数介绍
shell中允许将一组命令集合或语句形成一段可用代码,这些代码块称为shell函数。给这段代码起个名字称为函数名,后续可以直接调用该段代码的功能。可以将完成一个功能的一段代码进行命名、封装。
函数的优点:
a. 代码模块化,调用方便,节省内存
b. 代码模块化,代码量少,排错简单
c. 代码模块化,可以改变代码的执行顺序
2、函数定义
语法一:
函数名 () {
代码块
return N
}
语法二:
function 函数名 {
代码块
return N
}
函数中return说明:
a. return可以结束一个函数,类似于前面讲的循环控制语句break(结束当前循环,执行循环体后面的代码)
b. return默认返回函数中最后一个命令的退出状态,也可以给定参数值,该参数值的范围是0-256之间。
c. 如果没有return命令,函数将返回最后一个Shell的退出值。
3、函数调用
- 当前命令行调用
[root@localhost ~]# cat fun1.sh
#!/bin/bash
hello(){
echo "hello world $1"
hostname
}
menu(){
cat <<-EOF
1. mysql
2. web
3. app
4. exit
EOF
}
[root@localhost ~]# source fun1.sh
[root@localhost ~]# . fun1.sh
[root@localhost ~]# hello 888
hello zutuanxue 888
MissHou.zutuanxue.cc
[root@localhost ~]# menu
1. mysql
2. web
3. app
4. exit
- 定义到用户的环境变量中
/etc/profile /etc/bashrc ~/.bash_profile ~/.bashrc
[root@localhost ~]# cat ~/.bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
hello(){
echo "hello world $1"
hostname
}
menu(){
cat <<-EOF
1. mysql
2. web
3. app
4. exit
EOF
}
注意:
当用户打开bash的时候会读取该文件
- 脚本中调用
#!/bin/bash
#打印菜单
source ./fun1.sh
menu(){
cat <<-END
h 显示命令帮助
f 显示磁盘分区
d 显示磁盘挂载
m 查看内存使用
u 查看系统负载
q 退出程序
END
}
menu //调用函数
七、if 语句🌶
当我们在写程序的时候,时常对上一步执行是否成功如何判断苦恼,当我们今天学习了if就可以解决你的苦恼。if语句在我们程序中就是用来做判断的,以后大家不管学习什么语言,以后只要涉及到判断的部分,大家就可以直接拿if来使用,不同的语言之间的if只是语法不同,原理是相同的。
1、单if语法
适用范围:只需要一步判断,条件返回真干什么。
语句格式:
if [ condition ] #condition 值为true or false
then #条件为真的时候执行
commands #代码块 一行或者多行代码
fi #语句的结束
通过一段代码来演示一下吧,判断 当前用户是不是root,如果不是那么返回”ERROR: need to be root so that!“
#!/bin/bash
if [ $USER != 'root' ]
then
echo "ERROR: need to be root so that"
exit 1
fi
2、if…else 语句
适用范围:两步判断,条件为真干什么,条件为假干什么。
if [ condition ]
then #条件为真
commands1 #真 要执行代码块
else #条件为假
commands2 #假 要执行的代码块
fi #结束
通过一段代码演示一下吧,判断当前登录用户是管理员还是普通用户,如果是管理员输出”hey admin“ 如果是普通用户输出”hey guest“
#!/bin/bash
if [ $USER == 'root' ]
then
echo "hey admin"
else
echo "hey guest"
fi
3、if…elif…else 语句
适用范围:多于两个以上的判断结果,也就是多于一个以上的判断条件。
if [ condition 1] # 满足第一个条件
then # 真
command1 # 执行command1代码块
elif [ condition 2] # 满足第二个条件
then # 真
commands2 # 执行command2代码块
....... # ···
else # 如果条件都不满足
commandsX # 执行commandX代码块
fi # 结束判断
通过一段代码演示一下吧,比较两个整数的关系。
#!/bin/bash
if [ $1 -gt $2 ]
then
echo "$1 > $2"
elif [ $1 -eq $2 ]
then
echo "$1 = $2"
else
echo "$1 < $2"
fi
4、if高级用法
4.1、条件符号使用双圆括号,可以在条件中植入数学表达式 if (())
#!/bin/bash
if (( (5+5-5)*5/5 > 10 ))
then
echo "yes"
else
echo "no"
fi
4.2、使用双方括号,可以在条件中使用通配符
通过代码看下 ,为字符串提供高级功能,模式匹配 r* 匹配r开头的字符串
#!/bin/bash
for var in ab ac rx bx rvv vt
do
if [[ "$var" == r* ]]
then
echo "$var"
fi
done
5、简写if
省去了关键字,条件为真采用&&符号链接命令块,条件为假采用||链接命令块
简写if一般用在简单的判断中
if [ ! -d /tmp/baism ]
then
mkdir /tmp/baism
fi
#可以简写为:
[ ! -d /tmp/baism ] && mkdir /tmp/baism
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if [ $USER == 'root' ]
then
echo "hello root"
else
echo "hello guest"
fi
#可以简写为:
[ $USER == 'root' ]&&echo "hello root" || echo "hello guest"
6、if 判断
6.1 文件判断
# 常用的:
[ -a FILE ] 如果 FILE 存在则为真。
[ -d FILE ] 如果 FILE 存在且是一个目录则返回为真。
[ -e FILE ] 如果 指定的文件或目录存在时返回为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则返回为真。
[ -r FILE ] 如果 FILE 存在且是可读的则返回为真。
[ -w FILE ] 如果 FILE 存在且是可写的则返回为真。(一个目录为了它的内容被访问必然是可执行的)
[ -x FILE ] 如果 FILE 存在且是可执行的则返回为真。
# 不常用的:
[ -b FILE ] 如果 FILE 存在且是一个块文件则返回为真。
[ -c FILE ] 如果 FILE 存在且是一个字符文件则返回为真。
[ -g FILE ] 如果 FILE 存在且设置了SGID则返回为真。
[ -h FILE ] 如果 FILE 存在且是一个符号符号链接文件则返回为真。(该选项在一些老系统上无效)
[ -k FILE ] 如果 FILE 存在且已经设置了冒险位则返回为真。
[ -p FILE ] 如果 FILE 存并且是命令管道时返回为真。
[ -s FILE ] 如果 FILE 存在且大小非0时为真则返回为真。
[ -u FILE ] 如果 FILE 存在且设置了SUID位时返回为真。
[ -O FILE ] 如果 FILE 存在且属有效用户ID则返回为真。
[ -G FILE ] 如果 FILE 存在且默认组为当前组则返回为真。(只检查系统默认组)
[ -L FILE ] 如果 FILE 存在且是一个符号连接则返回为真。
[ -N FILE ] 如果 FILE 存在 and has been mod如果ied since it was last read则返回为真。
[ -S FILE ] 如果 FILE 存在且是一个套接字则返回为真。
[ FILE1 -nt FILE2 ] 如果 FILE1 比 FILE2 新, 或者 FILE1 存在但是 FILE2 不存在则返回为真。
[ FILE1 -ot FILE2 ] 如果 FILE1 比 FILE2 老, 或者 FILE2 存在但是 FILE1 不存在则返回为真。
[ FILE1 -ef FILE2 ] 如果 FILE1 和 FILE2 指向相同的设备和节点号则返回为真。
6.2 字符串判断
[ -z STRING ] 如果STRING的长度为零则返回为真,即空是真
[ -n STRING ] 如果STRING的长度非零则返回为真,即非空是真
[ STRING1 ] 如果字符串不为空则返回为真,与-n类似
[ STRING1 == STRING2 ] 如果两个字符串相同则返回为真
[ STRING1 != STRING2 ] 如果字符串不相同则返回为真
[ STRING1 < STRING2 ] 如果 “STRING1”字典排序在“STRING2”前面则返回为真。
[ STRING1 > STRING2 ] 如果 “STRING1”字典排序在“STRING2”后面则返回为真。
6.3 数值判断
[ INT1 -eq INT2 ] INT1和INT2两数相等返回为真 ,=
[ INT1 -ne INT2 ] INT1和INT2两数不等返回为真 ,<>
[ INT1 -gt INT2 ] INT1大于INT2返回为真 ,>
[ INT1 -ge INT2 ] INT1大于等于INT2返回为真,>=
[ INT1 -lt INT2 ] INT1小于INT2返回为真 ,<
[ INT1 -le INT2 ] INT1小于等于INT2返回为真,<=
6.4 文件权限判断
-r 当前用户对其是否可读
-w 当前用户对其是否可写
-x 当前用户对其是否可执行
-u 是否有suid
-g 是否sgid
-k 是否有t位
6.5 逻辑判断
&& (and 逻辑与) 两个条件同时满足,整个大条件为真
|| (or 逻辑或) 两个条件满足任意一个,整个大条件为真
! 非运算
注意:
1、[ ]表示条件测试。注意这里的空格很重要。要注意在'['后面和']'前面都必须要有空格
2、在shell中,then和fi是分开的语句。如果要在同一行里面输入,则需要用分号将他们隔开。
3、注意if判断中对于变量的处理,需要加引号,以免一些不必要的错误。没有加双引号会在一些含空格等的字符串变量判断的时候产生错误。比如[ -n "$var" ]如果var为空会出错
4、判断是不支持浮点值的
5、如果只单独使用>或者<号,系统会认为是输出或者输入重定向,虽然结果显示正确,但是其实是错误的,因此要对这些符号进行转意
6、在默认中,运行if语句中的命令所产生的错误信息仍然出现在脚本的输出结果中
7、使用-z或者-n来检查长度的时候,没有定义的变量也为0
8、空变量和没有初始化的变量可能会对shell脚本测试产生灾难性的影响,因此在不确定变量的内容的时候,在测试号前使用-n或者-z测试一下
9、? 变量包含了之前执行命令的退出状态(最近完成的前台进程)(可以用于检测退出状态)
八、循环语句-for🍄
1、for介绍
脚本在执行任务的时候,总会遇到需要循环执行的时候,比如说我们需要脚本每隔五分钟执行一次ping的操作,除了计划任务,我们还可以使用脚本来完成,那么我们就用到了循环语句。
2、for基本语法
- 列表for循环:用于将一组命令执行已知的次数,下面给出了for循环语句的基本格式:
for variable_name in {list}
do
command
command
…
done
# 或者
for variable in a b c
do
command
command
done
3、for条件应用
for条件不同的赋值方式
a、赋值来自一个范围
for var in {1..10}
do
echo $var
done
b、直接赋值
for var in 1 2 3 4 5
do
echo $var
done
c、赋值来自命令
for var in `seq 10`
do
echo $var
done
4、for C格式语法
类C风格的for循环
for(( expr1;expr2;expr3 ))
do
command
command
…
done
for (( i=1;i<=5;i++))
do
echo $i
done
expr1:定义变量并赋初值 变量初始值
expr2:决定是否进行循环(条件) 变量的条件
expr3:决定循环变量如何改变,决定循环什么时候退出 自增或自减运算
多变量用法
for ((A=1,B=10;A<10,B>1;A++,B--))
语法结构举例说明:
for ((i=1;i<=5;i++));do echo $i;done
for ((i=1;i<=10;i+=2));do echo $i;done
for ((i=2;i<=10;i+=2));do echo $i;done
5、for嵌套
5.1 for嵌套if
案例:
输出1-9,当输出5时停止输出
案例代码
#!/bin/bash
#Description:输出1-9,当输出5时停止输出
for ((num=1;num<10;num++))
do
echo $num
[ $num -eq 5 ]&& break
done
5.2 for嵌套for
案例:
打印99乘法表
案例代码
#!/bin/bash
#打印99乘法表,思考A*B的关系
for ((A=1;A<=9;A++))
do
for ((B=1;B<=$A;B++))
do
echo -n -e "$B*$A=$((A*B)) \t"
done
#换行
echo
done
6、for与数组
6.1 使用for循环遍历读出数组
name=('tom' 'jarry' 'harry' 'barry')
for i in 0 1 2 3
do
echo ${name[$i]}
done
6.2 使用for循环进行数组存值
for i in `seq 0 9`
do
read -p "name: " name[$i]
done
九、循环语句-while🥬
while在shell中也是负责循环的语句,和for一样。因为功能一样,很多人在学习和工作中的脚本遇到循环到底该使用for还是while呢?很多人不知道,就造就了有人一遇到循环就是for或者一位的while。我个人认为可以按照我说的这个思想来使用,既知道循环次数就可以用for,比如说一天需要循环24次;如果不知道代码要循环多少次,那就用while,比如我们作业中要求写的猜数字,每个人猜对一个数字的次数都是不能固定的,也是未知的。所以这样的循环我就建议大家用while了。
1、while介绍
特点:条件为真就进入循环;条件为假就退出循环,一般应用在未知循环次数的环境。
2、while语法
while [ 表达式 ]
do
command...
done
while [ 1 -eq 1 ] # 或者 (( 1 > 2 ))
do
command
command
...
done
案例
使用for循环和while循环分别循环打印数组1-5
案例代码
for循环打印:
for ((i=1;i<=5;i++))
do
echo $i
done
while循环打印:
#打印数字1-5
#!/bin/bash
#Description: 打印1-5
num=1
while [ $num -le 5 ]
do
echo $num
let num++
done
备注: 知道循环次数就可以用for,比如说一天需要循环24次;如果不知道代码要循环多少次,那就用while,比如我们作业中要求写的猜数字,每个人猜对一个数字的次数都是不能固定的,也是未知的。
3、while与shell运算
3.1 比较运算
案例:
循环交互输入一个小写字母,按Q退出循环
案例代码
#按Q退出场景
#!/bin/bash
read -p "请输入一个小写字母,按Q退出: " choose
while [ $choose != 'Q' ]
do
echo "你输入的是: $choose"
read -p "请输入一个小写字母,按Q退出: " choose
done
3.2 逻辑运算
案例:
使用循环语句帮助丈母娘批量选择女婿
案例代码
#!/bin/bash
#丈母娘选女婿 分别按照姑娘20 30 40 进行与或非模拟
#1.第一个应征者回答
read -p "你有多少钱: " money
read -p "你有多少车: " car
read -p "你家房子有几套: " house
#while [ $money -lt 10000 ]&&[ $car -lt 1 ]&&[ $house -lt 2 ]
while [ $money -lt 10000 ]||[ $car -lt 1 ]||[ $house -lt 2 ]
do
#应征者不满住条件开始下一次循环
echo "有请下一个"
read -p "你有多少钱: " money
read -p "你有多少车: " car
read -p "你家房子有几套: " house
done
#应征者满足条件
echo "乖女婿,你怎么才来啊!女儿给你了"
3.3 文件类型判断
案例:
使用循环判断/tmp/xxx目录下的文件,如果不是文件类型的打印字符串"目录"
案例代码
文件类型判断
#!/bin/bash
while [ ! -f /tmp/xxx ]
do
echo “目录”
sleep 1
done
3.4 特殊条件
while语句中可以使用特殊条件来进行循环:
- 符号":" 条件代表真,适用与无限循环
- 字符串 “true” 条件代表真,适用与无限循环
- 字符串 "false"条件代表假
代码展示
特殊符号 : 代表真
#!/bin/bash
while :
do
echo haha
sleep 1
done
true 字符串代表真,和:类似
#!/bin/bash
#
#Author: www.zutuanxue.com
#Created Time:
#Release:
#Description:
while true
do
echo haha
sleep 1
done
false 字符串代表假,在while中不会开始循环
4、while与循环控制语句
4.1 sleep语句
#!/bin/bash
#Description: 倒计时游戏
#1.定义初始值
time=9
#2.循环输出,1秒一次
while [ $time -ge 0 ]
do
echo -n -e "\b$time"
let time--
#控制循环 1秒一次
sleep 1
done
#回车
echo
4.2 break
#!/bin/bash
#Description: 输出数字1-9,当输出5时停止
#1、定义初始值
num=1
while [ $num -lt 10 ]
do
echo $num
#判断当前num的值,如果等于5就跳出循环
if [ $num -eq 5 ]
then
break
fi
#自动累加
let num++
done
4.3 continue
#!/bin/bash
#
#Author:
#Created Time:
#Release:
#Description: 输出数字1-9,当等于5时跳过本次循环,输出1、2、3、4、6、7、8、9
#1、定义初始值
num=0
while [ $num -lt 9 ]
do
#自动累加
let num++
#判断当前num的值,如果等于5就跳过本次循环
if [ $num -eq 5 ]
then
continue
fi
#输出num的值
echo $num
done
5、while嵌套其他语句
5.1 while嵌套if
#!/bin/bash
#Description: 输出数字1-9,当输出5时停止
#1、定义初始值
num=1
while [ $num -lt 10 ]
do
echo $num
#判断当前num的值,如果等于5就跳出循环
if [ $num -eq 5 ]
then
break
fi
#自动累加
let num++
done
5.2 while嵌套for
#!/bin/bash
#Description: 99乘法表
A=1
while [ $A -lt 10 ]
do
for ((B=1;B<=$A;B++))
do
echo -n -e "$B*$A=$((A*B)) \t"
done
echo
let A++
done
5.3 while嵌套while
#!/bin/bash
#Description: 99乘法表
#定义A
A=1
while [ $A -lt 10 ]
do
#定义B
B=1
while [ $B -le $A ]
do
echo -n -e "$B*$A=$((A*B)) \t"
let B++
done
echo
let A++
done
十、循环语句-until🫑
系统中还有一个类似while的循环语句,大家可以看看until语句,不同于while的是,当条件为假时开始until循环。
1、until介绍
特点:条件为假就进入循环;条件为真就退出循环
2、until语法
until expression [ 1 -eq 1 ] (( 1 >= 1 ))
do
command
command
...
done
案例:
使用while循环和until循环打印数字接龙,要求while循环输出1-5,until循环输出6-9.
案例代码
#!/bin/bash
#Description: 数字接龙
i=1
while [ $i -le 5 ]
do
echo $i
let i++
until [ $i -le 5 ]
do
echo $i
let i++
[ $i -eq 10 ]&&break
done
done
十一、条件循环语句-case🍌
在生产环境中,我们总会遇到一个问题需要根据不同的状况来执行不同的预案,那么我们要处理这样的问题就要首先根据可能出现的情况写出对应预案,根据出现的情况来加载不同的预案。
1、case介绍
特点:根据给予的不同条件执行不同的代码块
比如你去相亲:你会在脑子里出现以下的预案:
第一眼看到对方父亲,你应该说:伯父好
第一眼看到对方母亲,你应该说:伯母好
第一眼看到对方奶奶,你应该说:奶奶好
。。。。
而这个例子中触发就是你第一眼看到了对方的谁,预案则是叫什么称呼。
再来说一个计算机的相关例子---监控内存使用率
内存使用率低于80%,脚本输出: 绿色字体的Memory use xx%
内存使用率大于80%小于90%,脚本输出: 黄色字体的Memory use xx%
内存使用大于90%,脚本输出: 红色字体的Memory use xx%
2、case语法
case $var in 定义变量;var代表是变量名
pattern 1) 模式1;用 | 分割多个模式,相当于or
command1 需要执行的语句
;; 两个分号代表命令结束
pattern 2)
command2
;;
pattern 3)
command3
;;
*) default,不满足以上模式,默认执行*)下面的语句
command4
;;
esac esac表示case语句结束
3、案例
案例需求
写一个nginx启动管理脚本,可以实现/etc/init.d/nginx start|stop|restart|status|reload
或者
systemctl start nginx
#!/bin/bash
#nginx service manage script
#variables
nginx_install_doc=/usr/local/nginx
proc=nginx
nginxd=$nginx_install_doc/sbin/nginx
pid_file=$nginx_install_doc/logs/nginx.pid
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
else
echo "not found file /etc/init.d/functions"
exit
fi
#假如pid文件存在,那么统计一下nginx进程数量
if [ -f $pid_file ];then
nginx_process_id=`cat $pid_file`
nginx_process_num=`ps aux |grep $nginx_process_id|grep -v "grep"|wc -l`
fi
#function
start () {
#如果nginx 没有启动直接启动,否则报错 已经启动
if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ];then
echo "nginx running..."
else
#如果pid文件存在,但是没有进程,说明上一次非法关闭了nginx,造成pid文件没有自动删除,所以启动nginx之前先删除旧的pid文件
if [ -f $pid_file ] && [ $nginx_process_num -lt 1 ];then
rm -f $pig_file
#可以使用两个函数,两种方法来执行命令,并返回执行结果
#1)daemon
#2)action 建议这个,简单易用
#echo " nginx start `daemon $nginxd` "
action "nginx start" $nginxd
fi
#echo " nginx start `daemon $nginxd` "
action "nginx start" $nginxd
fi
}
stop () {
#判断nginx启动的情况下才会执行关闭,如果没启动直接报错,或者提示用户服务没启动,这里我直接报错的原因是为了给大家演示失败的输出
if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ];then
action "nginx stop" killall -s QUIT $proc
rm -f $pid_file
else
action "nginx stop" killall -s QUIT $proc 2>/dev/null
fi
}
restart () {
stop
sleep 1
start
}
reload () {
#重载的目的是让主进程重新加载配置文件,但是前提是服务必须开启
#这里先判断服务是否开启,开启就执行加载,没有开启直接报加载错误
if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ];then
action "nginx reload" killall -s HUP $proc
else
action "nginx reload" killall -s HUP $proc 2>/dev/null
fi
}
status () {
if [ -f $pid_file ]&&[ $nginx_process_num -ge 1 ];then
echo "nginx running..."
else
echo "nginx stop"
fi
}
#callable
case $1 in
start) start;;
stop) stop;;
restart) restart;;
reload) reload;;
status) status;;
*) echo "USAGE: $0 start|stop|restart|reload|status";;
esac
十二、循环控制🍍
1、break
作用: 终止循环,执行循环体后面的代码
案例:
循环打印输出数字1到9,当执行输出到5时终止循环。
案例代码
#!/bin/bash
for i in `seq 1 9`
do
echo $i
if [ $i -eq 5 ]
then
break
fi
done
echo "执行完毕"
2、continue
作用: 跳过某次循环,继续执行下一次循环;表示循环体内下面的代码不执行,重新开始下一次循环
案例:
循环打印输出数字1到9,当执行输出到5时跳过本次循环。
案例代码
#!/bin/bash
for ((i=1;i<10;i++))
do
if [ $i -eq 5 ]
then
continue
else
echo $i
fi
done
echo "执行完毕"
3、sleep
作用: 控制循环的节奏,控制循环频率
当执行一个无限循环语句的时候,如果任意其循环那么该循环就会疯狂的消耗计算机的内存和CPU资源,消耗最大的就是CPU,所以一个循环不可能让其肆意循环,必须控制其循环的节奏,可以使用sleep语句来完成。
案例:
写一个倒计时程序:从9到1,每秒倒数一次。
案例代码
#!/bin/bash
echo -n "倒计时: "
for i in `seq 9 -1 1`
do
echo -n -e "\b$i"
sleep 1
done
echo
echo "执行完毕"
4、shift
作用: 外部传参到循环时,参数管理命令
使位置参数向左移动,默认移动1位,可以使用shift 2 传参要是N的整数倍
案例:
通过外部传参的方式向脚本内的循环传递参数,要求打印每次循环使用的参数。
案例代码
#!/bin/bash
#1、判断外传参的数量
[ $# -lt 3 ]&&echo '请输入至少三个参数:'$0' $1 $2 $3 ...'&&exit 1
#将参数的数量赋值给count
count=$#
#通过shift左移参数输出
#使位置参数向左移动,默认移动1位,可以使用shift 2 传参要是N的整数倍
for ((i=1;i<=$count;i++))
do
echo '参数数量: '$#''
echo '当前$1的数值是: '$1''
shift 1
sleep 1
done
echo "执行完毕"
5、exit
作用: 退出程序并释放占用的系统资源。
案例:
循环输出数字1-9,当循环到5时退出脚本。
案例代码
#!/bin/bash
for i in `seq 1 9`
do
echo $i
if [ $i -eq 5 ]
then
exit 0
fi
done
echo "执行完毕"
十三、 正则表达式🍓
1、正则表达式介绍
正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。
正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。
支持正则表达式的程序如:locate |find| vim| grep| sed |awk
正则表达式是一个三方产品,被常用计算机语言广泛使用,比如:shell、PHP、python、java、js等!
2、正则表达式特殊字符
定位符使用技巧:同时锚定开头和结尾,做精确匹配;单一锚定开头或结尾或者不锚定的,做模糊匹配。
定位符 | 说明 |
---|---|
^ | 锚定开头 ^a 以a开头 默认锚定一个字符 |
$ | 锚定结尾 a$ 以a结尾 默认锚定一个字符 |
测试案例
1)精确匹配 以a开头c结尾的字符串
[root@localhost ~]# egrep "^ac$" file
ac
2)模糊匹配 以a开头
[root@localhost ~]# egrep "^a" file
ac
ab
3)模糊匹配 以c结尾的字符串
[root@localhost ~]# egrep "c$" file
ac
abc
匹配符:匹配字符串
匹配符 | 说明 |
---|---|
. | 匹配除回车以外的任意一个字符 |
( ) | 字符串分组 |
[ ] | 定义字符类,匹配括号中的一个字符 |
[ ^ ] | 表示否定括号中出现字符类中的字符,取反。 |
\ | 转义字符 |
| | 或 |
测试案例
1)精确匹配 以a开头c结尾 中间任意 长度为三个字节的字符串
[root@localhost ~]# egrep "^a.c$" file
acc
abc
a_c
2)模糊匹配 以cc结尾的字符串 因为$只能锚定单个字符,如果是一个字符串就需要用()来做定义
[root@localhost ~]# egrep "(cc)$" file
abcc
aabbcc
acc
ccc
3)精确匹配 以a开头c结尾 中间是a-z,0-9 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[a-z0-9]c$" file
acc
abc
a3c
4)精确匹配 以a开头c结尾 中间不包含a-z,0-9 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[^a-z0-9]c$" file
a_c
aZc
a c
5)精确匹配 以e开头f结尾 中间是*号 长度为三个字节的字符串 e*f
[root@localhost ~]# egrep "^e\*f$" file
e*f
6)精确匹配 以a开头b或c结尾 中间是任意 长度为三个字节的字符串
[root@localhost ~]# egrep "^a.(b|c)$" file
acc
abc
asb
a_c
aZc
a c
a3c
限定符:对前面的字符或者(字符串)出现的次数做限定说明
限定符 | 说明 |
---|---|
* | 某个字符之后加星号表示该字符不出现或出现多次 a (ab) |
? | 与星号相似,但略有变化,表示该字符出现一次或不出现 |
+ | 与星号相似,表示其前面字符出现一次或多次,但必须出现一次 |
{n,m} | 某个字符之后出现,表示该字符最少n次,最多m次 |
{m} | 正好出现了m次 |
测试案例
1)精确匹配 以a开头 c结尾 中间是有b或者没有b 长度不限的字符串
[root@localhost ~]# egrep "^ab*c$" file
ac
abbc
abbbc
abbbbbc
abc
2)精确匹配 以a开头 c结尾 中间只出现一次b或者没有b的字符串
[root@localhost ~]# egrep "^ab?c$" file
ac
abc
3)精确匹配 以a开头 c结尾 中间是有b且至少出现一次 长度不限的字符串
[root@localhost ~]# egrep "^ab+c$" file
abbc
abbbc
abbbbbc
abc
4)精确匹配 以a开头 c结尾 中间是有b且至少出现两次最多出现四次 长度不限的字符串
[root@localhost ~]# egrep "^ab{2,4}c$" file
abbc
abbbc
5)精确匹配 以a开头 c结尾 中间是有b且正好出现三次的字符串
[root@localhost ~]# egrep "^ab{3}c$" file
abbbc
6) 精确匹配 以a开头 c结尾 中间是有b且至少出现一次的字符串
[root@localhost ~]# egrep "^ab{1,}c$" file
abbc
abbbc
abbbbbc
abc
3、正则表达式POSIX字符
posix字符一次只匹配一个范围中的一个字节
特殊字符 | 说明 |
---|---|
[:alnum:] | 匹配任意字母字符0-9 a-z A-Z |
[:alpha:] | 匹配任意字母,大写或小写 |
[:digit:] | 数字 0-9 |
[:graph:] | 非空字符( 非空格控制字符) |
[:lower:] | 小写字符a-z |
[:upper:] | 大写字符A-Z |
[:cntrl:] | 控制字符 |
[:print:] | 非空字符( 包括空格) |
[:punct:] | 标点符号 |
[:blank:] | 空格和TAB字符 |
[:xdigit:] | 16 进制数字 |
[:space:] | 所有空白字符( 新行、空格、制表符) |
测试案例
注意[[ ]] 双中括号的意思: 第一个中括号是匹配符[] 匹配中括号中的任意一个字符,第二个[]是格式 如[:digit:]
1)精确匹配 以a开头c结尾 中间a-zA-Z0-9任意字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:alnum:]]c$" file
acc
abc
aZc
a3c
2)精确匹配 以a开头c结尾 中间是a-zA-Z任意字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:alpha:]]c$" file
acc
abc
aZc
3)精确匹配 以a开头c结尾 中间是0-9任意字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:digit:]]c$" file
a3c
4)精确匹配 以a开头c结尾 中间是a-z任意字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:lower:]]c$" file
acc
abc
4)精确匹配 以a开头c结尾 中间是A-Z任意字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:upper:]]c$" file
aZc
5)精确匹配 以a开头c结尾 中间是非空任意字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:print:]]c$" file
acc
abc
a_c
aZc
a c
a3c
6)精确匹配 以a开头c结尾 中间是符号字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:punct:]]c$" file
a_c
7)精确匹配 以a开头c结尾 中间是空格或者TAB符字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:blank:]]c$" file
a c
类似
[root@localhost ~]# egrep "^a[[:space:]]c$" file
a c
8)精确匹配 以a开头c结尾 中间是十六进制字符 长度为三个字节的字符串
[root@localhost ~]# egrep "^a[[:xdigit:]]c$" file
acc
abc
a3c
说明:特殊字符和POSIX字符是两套字符,都可以完成需要的匹配,大家学习的时候最少要记住一套字符并熟练应用。
请问 这是什么程序源码啊