大家好,又见面了,我是你们的朋友全栈君。
文章目录
- 1. shell 概述
- 1.1 shell 和 shell 脚本
- 1.2 Shell 脚本应用场景
- 1.3 Linux 中的 shell 类型
- 1.4 脚本实例
- 2. shell 脚本编程基础
- 2.1 脚本调试
- 2.2 脚本执行方法
- 2.3 管道符号 |
- 2.4 交互式硬件设备
- 2.5 重定向操作
- 2.6 变量
- (1) 变量的赋值与引用
- ① 变量赋值
- ② 变量引用
- (2) 变量类型
- ① 自定义变量
- ② 环境变量
- ③ 只读变量
- ④ 位置变量
- ⑤ 预定义变量
- (3) 变量的查看与删除
- ① 查看已定义的所有变量
- ② 删除变量
- (4) 变量的赋值运算
- (1) 变量的赋值与引用
- 2.7 算数运算
- (1) 实现算数运算的方式
- (2) 增强型赋值
- (3) 逻辑运算
- (4) 随机数生成器变量 — $RANDOM
- (5) “空” 设备 — /dev/null
- (6) “零” 设备 — /dev/zero
- (7) 随机数生成器设备 — /dev/random /dev/urandom
- 2.8 echo -n/-e 的用法
- 2.9 条件测试命令
- 2.10 流程控制
- (1) if 条件语句
- (2) case 多分支语句
- (3) for 循环
- (4) while 循环
- (5) until 循环
- (6) 循环控制语句 continue
- (7) 循环控制语句 break
- (8) 循环控制 shift 命令
- (9) while read 特殊用法
- (10) select 循环与菜单
- 2.11 Shell 函数
- (1) 函数介绍
- (2) 函数管理
- ① 定义函数
- ② 查看函数
- ③ 删除函数
- (3) 函数调用
- ① 交互式环境调用函数
- ② 在脚本中定义及使用函数
- ③ 使用函数文件
- (4) 函数返回值
- (5) 环境函数
- (6) 函数参数
- (7) 函数变量
- (8) 函数递归
- 2.12 一些脚本相关工具
- (1) 信号捕捉 trap
- (2) 创建临时文件 mktemp
- (3) 安装复制文件 install
- (4) 交互式转化批处理工具 expect
- 2.13 数组
- (1) 数组介绍
- (2) 声明数组
- (3) 数组赋值
- ① 一次只赋值一个元素
- ② 一次赋值全部元素
- ③ 只赋值特定元素
- ④ 交互式数组值对赋值
- (4) 显示所有数组
- (5) 引用数组
- ① 引用数组元素
- ② 引用数组所有元素
- ③ 数组的长度,即数组中元素的个数
- (6) 删除数组
- ① 删除数组中的某个元素
- ② 删除整个数组
- (7) 数组数据处理
- ① 数组切片
- ② 向数组中追加元素
- (8) 关联数组
- (9) 数组脚本示例
1. shell 概述
1.1 shell 和 shell 脚本
shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务,在用户和内核之间充当翻译官的角色,是一个命令解释器。Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 shell。
shell 脚本(shell script),是一种为 shell 编写的脚本程序。业界所说的 shell 通常都是指 shell 脚本,shell 和 shell script 是两个不同的概念。
1.2 Shell 脚本应用场景
- 重复性操作
- 交互性任务
- 批量事务处理
- 服务运行状态监控
- 定时任务执行 …
1.3 Linux 中的 shell 类型
以 CentOS 7 为例
代码语言:javascript复制[root@c7-1 ~]#cat /etc/shells
/bin/sh #/bin/sh 是 bash 命令的软链接(已经被 /bin/bash 所替换)
/bin/bash #基准于 GNU 的框架下发展出的 shell
/usr/bin/sh #己经被 bash 所替换
/usr/bin/bash #centos 和 redhat 系统默认使用 bash shell
/bin/tcsh #csh 的增强版,与 csh 完全兼容,提供更多的功能
/bin/csh #已经被 /bin/bash 所替换(整合 c shell,提供更多的功能)
/sbin/nologin #奇怪的 shel1,这个 shell 可以让用户无法登录主机
1.4 脚本实例
代码语言:javascript复制[root@c7-1 ~]#curl -s http://47.117.130.238/hello.sh
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 2021/06/06
# Author: gongboyi
# Email: 1520509800@qq.com
# Website: https://www.cnblogs.com/shenyuanhaojie/
# Description: This is the first script
# Copyright: 2021 zhou
# License: GPL
# ------------------------------------------
echo "hello world!"
[root@c7-1 ~]#curl -s http://47.117.130.238/hello.sh|bash
hello world!
2. shell 脚本编程基础
2.1 脚本调试
只检测脚本中的语法错误,无法检查出命令错误,不真正执行脚本
代码语言:javascript复制bash -n /path/to/script.sh
调试并执行
代码语言:javascript复制bash -x /path/to/script.sh
脚本错误常见的有三种:
代码语言:javascript复制语法错误:会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准确的。
命令错误:后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察。
逻辑错误:只能使用 bash -x 进行观察。
2.2 脚本执行方法
代码语言:javascript复制相对路径执行( ./script.sh )在脚本当前目录,脚本需要执行权限
绝对路径执行( /PATH/to/script.sh )无需在脚本目录,脚本需要执行权限
bash 执行( bash /PATH/to/script.sh )bash 后可跟绝对路径和相对路径,脚本无需执行权限
source 执行( source /PATH/to/script.sh )source 后可跟绝对路径和相对路径,脚本无需执行权限
bash < script.sh 或 cat script.sh | bash
示例:
代码语言:javascript复制[root@aliyunhost01html]#./hello.sh
hello world!
[root@aliyunhost01html]#/var/www/html/1.sh
hello world
[root@aliyunhost01html]#bash /var/www/html/1.sh
hello world
[root@aliyunhost01html]#source /var/www/html/1.sh
hello world
[root@aliyunhost01html]#bash < /var/www/html/1.sh
hello world
[root@aliyunhost01html]#cat /var/www/html/1.sh | bash
hello world
2.3 管道符号 |
将左侧命令的输出结果作为右侧命令的处理对象。 示例:
代码语言:javascript复制[root@c7-1 ~]#grep "/sbin/nologin" /etc/passwd | awk -F: '{print $1,$7}'
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
mail /sbin/nologin
......
2.4 交互式硬件设备
代码语言:javascript复制标准输入:从该设备接收用户输入的数据
标准输出:通过该设备向用户输出数据
标准错误:通过该设备报告执行出错信息
类型 | 设备文件 | 文件描述编号 | 默认设备 |
---|---|---|---|
标准输入 | /dev/stdin | 0 | 键盘 |
标准输出 | /dev/stdout | 1 | 显示器 |
标准错误输出 | /dev/stderr | 2 | 显示器 |
[root@c7-1 ~]#ll /dev/std*
lrwxrwxrwx 1 root root 15 9月 2 10:02 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 9月 2 10:02 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 9月 2 10:02 /dev/stdout -> /proc/self/fd/1
2.5 重定向操作
类型 | 操作符 | 用途 |
---|---|---|
重定向输入 | < | 从指定的文件读取数据,而不是从键盘输入 |
重定向输出 | > | 将输出结果保存到指定的文件(覆盖原有内容) |
>> | 将输出结果追加到指定的文件尾部 | |
标准错误输出 | 2> | 将错误信息保存到指定的文件(覆盖原有内容) |
2>> | 将错误信息追加到指定的文件中 | |
混合输出 | &> | 将标准输出、标准错误的输出保存到同一个文件中 |
示例:
代码语言:javascript复制#将 ./test.sh 的输出重定向到 log.txt 文件中,同时将标准错误也重定向到 log.txt 文件中
./test.sh > log.txt 2>&1
# nohup ... & 代表后台运行并且生成 nohup.log 日志文件
# command>/dev/null 代表命令输出结果导入到空设备
# 2>&1 代表将标准错误的内容重定向到标准输出,即将程序运行中的错误信息也打印出来
nohup ./test.sh>/dev/null 2>&1 &
参考:重定向理解
2.6 变量
(1) 变量的赋值与引用
① 变量赋值
代码语言:javascript复制name='value'
value 可以是以下多种形式:
代码语言:javascript复制直接字串:name='root'
变量引用:name="$USER"
命令引用:name=`COMMAND` 或者 name=$(COMMAND)
变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量随着脚本结束,也会自动删除。
使用 read 从标准输入读取数值:
代码语言:javascript复制read -p "提示信息" [变量名]
read -p "提示信息" [变量名] < [文件]
常用选项:
代码语言:javascript复制-p #指定要显示的提示信息
-s #静默输入,一般用于密码
-n N #指定输入的字符长度 N
-d '字符' #输入结束符
-t N #TIMEOUT 为 N 秒
示例:
代码语言:javascript复制[root@c7-1 ~]#read -p "请输入密码:" passwd
请输入密码:120604
[root@c7-1 ~]#echo $passwd
120604
[root@c7-1 ~]#vim IP.txt
[root@c7-1 ~]#read -p "请输入IP地址:" IP < IP.txt
[root@c7-1 ~]#echo $IP
47.117.130.238
[root@c7-1 ~]#cat IP.txt
47.117.130.238
#实现运维工作菜单
echo -en "E[$[RANDOM%7 31];1m"
cat <<EOF 请选择: 1)备份数据库 2)清理日志 3)软件升级 4)软件回滚 5)删库跑路 EOF
echo -en 'E[0m'
read -p "请输入上面数字1-5: " MENU
[ $MENU -eq 1 ] && ./backup.sh
[ $MENU -eq 2 ] && echo "清理日志"
[ $MENU -eq 3 ] && echo "软件升级"
[ $MENU -eq 4 ] && echo "软件回滚"
[ $MENU -eq 5 ] && echo "删库跑路"
② 变量引用
代码语言:javascript复制" " 弱引用,允许通过 $ 符号引用其他变量值
' ' 强引用,禁止引用其他变量值,$ 视为普通字符
` ` 命令引用,提取命令执行后的输出结果
示例:
代码语言:javascript复制[root@c7-1 ~]#username=zc
[root@c7-1 ~]#echo "you are $username"
you are zc
[root@c7-1 ~]#echo 'you are $username'
you are $username
[root@c7-1 ~]#echo `whoami`
root
(2) 变量类型
- 自定义变量:由用户自己定义、修改、使用,只在用户自己的 shell 环境中有效,因此又称为本地变量
- 特殊变量:环境变量、只读变量、位置变量、预定义变量
① 自定义变量
基本格式:
代码语言:javascript复制变量名=变量值
示例:
代码语言:javascript复制[root@c7-1 ~]#username=zc
[root@c7-1 ~]#echo $username
zc
[root@c7-1 ~]#set | grep username #set 显示用户自定义的变量
username=zc
local remote_opts="--username= --config-dir= --no-auth-cache";
--no-auth-cache --username=
[root@c7-1 ~]#unset username
[root@c7-1 ~]#set | grep username
_=username
local remote_opts="--username= --config-dir= --no-auth-cache";
--no-auth-cache --username=
② 环境变量
环境变量分为:
- 全局变量:不仅对 shell 可见,对其子进程也可见
- 局部变量:只能在定义其的进程中可见
显示所有环境变量:
代码语言:javascript复制env
printenv
export
declare -x
Bash 内建的环境变量:
代码语言:javascript复制PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell 的嵌套层数
LANG
MAIL
HOSTNAME
HISTSIZE
_ #下划线,表示前一命令的最后一个参数
声明全局变量:
代码语言:javascript复制export [变量名]
declare -x [变量名]
声明的全局变量虽然在环境变化的时候仍然生效,但是当系统重启后不会保存,要想永久生效可以保存到配置文件中。
变量配置文件:
代码语言:javascript复制系统层面:/etc/profile、/etc/bashrc
用户层面:~/.bash_profile、~/.bashrc、~/.bash_history、~/.bash_logout
#系统层面的配置文件通常在登录时加载,用户层面的配置文件只对单个用户生效
参考:Linux环境变量配置全攻略
③ 只读变量
只读变量的声明:
代码语言:javascript复制readonly [变量名]
declare -r [变量名]
查看:
代码语言:javascript复制readonly [-p]
declare -r
只读变量无法删除或更改,当退出登录或者重启系统会失效。
示例:
代码语言:javascript复制[root@c7-1 ~]#readonly PI=3.14
[root@c7-1 ~]#echo $PI
3.14
[root@c7-1 ~]#PI=3.14159
-bash: PI: 只读变量
[root@c7-1 ~]#unset PI
-bash: unset: PI: 无法反设定: 只读 variable
[root@c7-1 ~]#logout
Connection closed by foreign host.
Disconnected from remote host(c7-1) at 21:53:33.
Type `help' to learn how to use Xshell prompt.
[c:~]$
Reconnecting in 5 seconds. Press any key to exit local shell.
...
Connecting to 192.168.10.20:22...
Connection established.
To escape to local shell, press Ctrl Alt ].
Last login: Fri Sep 3 21:33:20 2021 from 192.168.10.1
[root@c7-1 ~]#echo $PI
[root@c7-1 ~]#
④ 位置变量
在 bash shell 中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数。
代码语言:javascript复制$1, $2, ... 对应第1个、第2个等参数,shift [n] 换位置
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异
清空所有位置变量:
代码语言:javascript复制set --
示例:
代码语言:javascript复制[root@c7-1 ~]#cat test.sh
echo $1 $2 $3
echo $0
echo $?
echo $$
echo $!
echo $#
echo $*
echo $@
[root@c7-1 ~]#bash test.sh a b c
a b c
test.sh
0
72999
3
a b c
a b c
#实现添加用户
[root@c7-1 ~]#cat useradd.sh
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
[root@c7-1 ~]#bash useradd.sh zc 123456
更改用户 zc 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@c7-1 ~]#cat /etc/passwd|grep zc
zc:x:1001:1001::/home/zc:/bin/bash
#删库跑路之命令 rm 的安全实现
WARNING_COLOR="echo -e E[1;31m"
END="E[0m"
DIR=/tmp/`date %F_%H-%M-%S`
mkdir $DIR
mv $* $DIR
${WARNING_COLOR}Move $* to $DIR $END
⑤ 预定义变量
预定义变量是在 Shell 一开始时就定义的变量,这一点和默认环境变量有些类似。不同的是,预定义变量不能重新定义,用户只能根据 Shell 的定义来使用这些变量。严格来说,位置参数变量也是预定义变量的一种,只是位置参数变量的作用比较统一,所以我们把位置参数变量单独划分为一类数量。
代码语言:javascript复制$? 表示前一条命令执行后的返回状态,返回值为О表示执行正确,返回任何非О值均表示执行出现异常
$$ 表示返回当前进程的进程号
$! 返回最后一个后台进程的进程号
用户可以在脚本中使用以下命令自定义退出状态码:
代码语言:javascript复制exit [n]
#脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
#如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
示例:
代码语言:javascript复制#测试网络通信
[root@c7-1 ~]#ping -c1 -W1 47.117.130.238 &> /dev/null
[root@c7-1 ~]#echo $?
0
#测试服务端口
[root@aliyunhost01~]#curl 127.0.0.1:80 &> /dev/null
[root@aliyunhost01~]#echo $?
0
(3) 变量的查看与删除
① 查看已定义的所有变量
代码语言:javascript复制set
② 删除变量
代码语言:javascript复制unset [变量名]
#只读变量无法删除
(4) 变量的赋值运算
代码语言:javascript复制 加
- 减
* 乘
/ 除
% 取模,即取余数,示例:9%4=1,5%3=2
** 乘方
示例:
代码语言:javascript复制[root@c7-1 ~]#expr 2 3
5
[root@c7-1 ~]#num1=2
[root@c7-1 ~]#num2=3
[root@c7-1 ~]#expr $num1 $num2
5
[root@c7-1 ~]#expr $num2 / $num1
1
[root@c7-1 ~]#expr $num1 * $num2
6
[root@c7-1 ~]#expr $num2 % $num1
1
[root@c7-1 ~]#sum=$(($num1**$num2))
[root@c7-1 ~]#echo $sum
8
2.7 算数运算
(1) 实现算数运算的方式
代码语言:javascript复制let var=算术表达式
((var=算术表达式))
var=$[算术表达式]
var=$((算术表达式))
var=$(expr arg1 arg2 arg3 ...)
declare -i var = 数值
echo '算术表达式' | bc
(2) 增强型赋值
代码语言:javascript复制 = # i =j 相当于 i=i j
-= # i-=j 相当于 i=i-j
*= # i*=j 相当于 i=i*j
/= # i/=j 相当于 i=i/j
%= # i%=j 相当于 i=i%j
# i , i 相当于 i=i 1
-- # i--,--i 相当于 i=i-1
说明:i 和 i,i– 和 –i 含义是不同的,我们用具体的示例来解释
代码语言:javascript复制# i 先将值赋值给了 j ,然后再完成自加
[root@c7-1 ~]#unset i j; i=1; let j=i ; echo "i=$i,j=$j"
i=2,j=1
# i 完成自加后赋值给 j
[root@c7-1 ~]#unset i j; i=1; let j= i; echo "i=$i,j=$j"
i=2,j=2
(3) 逻辑运算
代码语言:javascript复制布尔型变量bool的取值只有false和true
true 1
false 0
& 与
代码语言:javascript复制 1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
| 或
代码语言:javascript复制 1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
! 非
代码语言:javascript复制! 1 = 0
! 0 = 1
^ 异或
代码语言:javascript复制相同为假,不同为真
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
&& 短路与
代码语言:javascript复制CMD1 短路与 CMD2
第一个CMD1结果为真(1),第二个CMD2必须要参与运算,才能得到最终的结果
第一个CMD1结果为假(0),总的结果必定为0,因此不需要执行CMD2
|| 短路或
代码语言:javascript复制CMD1 短路或 CMD2
第一个CMD1结果为真(1),总的结果必定为1,因此不需要执行CMD2
第一个CMD1结果为假(0),第二个CMD2必须要参与运算,才能得到最终的结果
(4) 随机数生成器变量 – $RANDOM
bash 有内建的随机数生成器:$RANDOM(0-32767)
代码语言:javascript复制#生成 0-49 之间随机数
echo $[$RANDOMP]
#更换字体颜色,每次输出的结果显示不同的颜色
echo -e "E[1;$[RANDOM%7 31]m hello e[0m"
(5) “空” 设备 – /dev/null
“空” 设备,也被称为黑洞。任何输入到这个设备的数据都将被直接丢弃(但是操作返回成功 $? 值为 0)。最常用的用法是把不需要的输出重定向到这个文件。例如:
代码语言:javascript复制#将标准输出和错误输出重定向到 /dev/null,运行这个脚本不会输出任何信息到终端
run.sh 1>/dev/null 2>&1
(6) “零” 设备 – /dev/zero
“零” 设备,可以无限的提供空字符(0x00,ASCII代码NUL)。常用来生成一个特定大小的文件。例如:
代码语言:javascript复制#产生一个 1k 大小的文件 output.txt
dd if=/dev/zero of=./output.txt bs=1024 count=1
(7) 随机数生成器设备 – /dev/random /dev/urandom
随机数设备,提供不间断的随机字节流。二者的区别是 /dev/random 产生随机数据依赖系统中断,当系统中断不足时,/dev/random 设备会 “挂起”,因而产生数据速度较慢,但随机性好;/dev/urandom 不依赖系统中断,数据产生速度快,但随机性较低。
代码语言:javascript复制#生成随机密码
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;
openssl rand -base64 32
date %s | sha256sum | base64 | head -c 32 ; echo
tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1
strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 30 | tr -d 'n'; echo
dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev
date | md5sum
参考:Linux 中的虚拟设备
2.8 echo -n/-e 的用法
echo -n 不换行输出
代码语言:javascript复制[root@c7-1 ~]#echo "123456"
123456
[root@c7-1 ~]#echo -n "123456"
123456[root@c7-1 ~]#
echo -e 处理特殊字符
代码语言:javascript复制若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出
a 发出警告声
b 删除前一个字符
c 最后不加上换行符号
f 换行但光标仍旧停留在原来的位置
n 换行且光标移至行首
r 光标移至行首,但不换行
t 插入tab
v 与f相同
\ 插入字符
nnn 插入nnn(八进制)所代表的ASCII字符
参考: echo -n/-e 用法 echo 命令详解 echo 显示内容带颜色
2.9 条件测试命令
判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程。
代码语言:javascript复制若真,则状态码变量 $? 返回 0
若假,则状态码变量 $? 返回 1
条件测试格式:
代码语言:javascript复制test <选项> <测试的内容>
[ 选项 测试的内容 ]
[[ 选项 测试的内容 ]]
常用测试选项:
代码语言:javascript复制-e 测试目录或文件是否存在,[ -a file ] 等于 [ -e file ]
-d 测试是否为目录
-f 测试是否为文件
-r 测试当前用户是否有权限读取
-w 测试当前用户是否有权限写入
-x 测试当前用户是否有权限执行
-u 测试文件是否存在且拥有 suid 权限
-g 测试文件是否存在且拥有 sgid 权限
-k 测试文件是否存在且拥有 sticky 权限
-z 如果 STRING 的长度为零则为真
-n 如果 STRING 的长度非零则为真
-b 测试文件是否存在并且是块设备文件
-c 测试文件是否存在并且是字符设备文件
-L 测试文件是否存在并且是链接文件
-p 测试文件是否存在并且是管道文件
-S 测试文件是否存在并且是套接字文件
-s 测试文件是否存在并且文件大小为空
其他文件属性测试选项:
代码语言:javascript复制-t fd fd 文件描述符是否在某终端已经打开
-N FILE 文件自从上一次被读取之后是否被修改过
-O FILE 当前有效用户是否为文件属主
-G FILE 当前有效用户是否为文件属组
FILE1 -ef FILE2 FILE1 是否是 FILE2 的硬链接
FILE1 -nt FILE2 FILE1 是否新于 FILE2(mtime)
FILE1 -ot FILE2 FILE1 是否旧于 FILE2
字符串比较:
代码语言:javascript复制[ STRING1 = STRING2 ] 两个字符串相等为真
[ STRING1 != STRING2 ] 两个字符串不等为真
[ STRING1 < STRING2 ] 如果 STRING1 按字典顺序排在 STRING2 之前,则为真
[ STRING1 > STRING2 ] 如果 STRING1 按字典顺序排在 STRING2 之后,则为真
[[ STRING1 == STRING2 ]] 左侧字符串是否和右侧的 PATTERN 相同
[[ STRING1 =~ STRING2 ]] 左侧字符串是否能够被右侧的正则表达式的 PATTERN 所匹配
逻辑运算与、或、非(条件组合测试):
代码语言:javascript复制[ ! EXPR ] 逻辑非,与表达相反的结果为真
[ EXPR1 -a EXPR2 ] 逻辑与,要同时满足多个表达式才为真,等同于 &&
[ EXPR1 -o EXPR2 ] 逻辑或,满足其中任意一个表达式即为真,等同于 ||
#如果 && 和 || 混合使用,&& 要在前,|| 在后
变量测试:
代码语言:javascript复制-v VAR 变量 VAR 是否设置
-r VAR 变量 VAR 是否设置并引用
数值比较:
代码语言:javascript复制-eq 是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于
2.10 流程控制
(1) if 条件语句
格式:
代码语言:javascript复制if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else
COMMANDS; ] fi
单分支:
代码语言:javascript复制if 判断条件;then
条件为真的分支代码
fi
双分支:
代码语言:javascript复制if 判断条件;then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支:
代码语言:javascript复制if 判断条件1;then
条件1为真的分支代码
elif 判断条件2;then
条件2为真的分支代码
elif 判断条件3;then
条件3为真的分支代码
...
else
以上条件都为假的分支代码
fi
示例:
代码语言:javascript复制###单分支
[root@c7-1 ~]#cat if1.sh
#!/bin/bash
if ping -c1 -w2 www.baidu.com &> /dev/null;then
echo "网络连通"
fi
[root@c7-1 ~]#bash if1.sh
网络连通
###双分支
[root@c7-1 ~]#cat if2.sh
#!/bin/bash
IP=47.117.130.238
if ping -c1 -i 0.2 -w2 $IP &> /dev/null;then
echo "主机在线"
else
echo "主机断联"
fi
[root@c7-1 ~]#bash if2.sh
主机在线
###多分支
[root@c7-1 ~]#cat if3.sh
#!/bin/bash
read -p "请输入分数:" GRADE
if [ $GRADE -le 100 -a $GRADE -ge 80 ];then
echo "成绩优秀"
elif [ $GRADE -lt 80 -a $GRADE -ge 60 ];then
echo "成绩合格"
else
echo "不及格"
fi
[root@c7-1 ~]#bash if3.sh
请输入分数:65
成绩合格
(2) case 多分支语句
格式:
代码语言:javascript复制case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
分支结构:
代码语言:javascript复制case 变量值 in
模式一)
命令序列
;;
模式二)
命令序列
;;
模式三)
命令序列
;;
......
*)
默认命令序列
esac
case 支持 glob 风格的通配符:
代码语言:javascript复制* 任意长度任意字符
? 任意单个字符
[] 指定范围内的任意单个字符
| 逻辑或
示例:
代码语言:javascript复制###判断输入yes或no
[root@c7-1 ~]#cat YESorNO.sh
#!/bin/bash
read -p "请输入YES或NO:" VAR
VAR=`echo $VAR | tr 'A-Z' 'a-z'`
case $VAR in
y|ye|ys|yes)
echo "你输入的是YES"
;;
n|no)
echo "你输入的是NO"
;;
*)
echo "输入错误,请重新输入YES或NO"
esac
[root@c7-1 ~]#bash YESorNO.sh
请输入YES或NO:y
你输入的是YES
###httpd 服务控制
[root@c7-1 ~]#cat httpd.sh
#!/bin/bash
case $1 in
start)
/usr/bin/systemctl $1 httpd
/usr/bin/ps aux | grep httpd
echo "httpd start"
;;
stop)
/usr/bin/systemctl $1 httpd
/usr/bin/ps aux | grep httpd
echo "httpd stop"
;;
restart)
echo "httpd stop......"
/usr/bin/ps aux | grep httpd
/usr/bin/systemctl $1 httpd
echo "httpd restart......"
/usr/bin/ps aux | grep httpd
;;
status)
/usr/bin/systemctl $1 httpd
;;
*)
echo "请输入 start|stop|restart|status"
esac
[root@c7-1 ~]#./httpd.sh status | grep active
Active: active (running) since 一 2021-09-06 11:20:30 CST; 35s ago
(3) for 循环
格式:
代码语言:javascript复制#格式一
for 变量名 in 取值列表;
do
循环体
done
#格式二
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done
示例:
代码语言:javascript复制#!/bin/bash
for i in `seq 10`
do
echo $i
done
————————————————————
#!/bin/bash
#输出 10 次 hello world
a=10
for i in `seq $a`
do
echo "hello world"
done
————————————————————
#!/bin/bash
#输出 0-10,间隔 2
for i in {
0..10..2}
do
echo $i
done
————————————————————
#!/bin/bash
for ((i=1;i<=5;i ))
do
echo $i
done
————————————————————
#!/bin/bash
#批量创建用户并修改密码
for i in {
1..5}
do
useradd user$i
echo "123456" | passwd --stdin user$i
done
————————————————————
#!/bin/bash
#根据用户列表文件批量创建用户并修改密码
ULIST=$(cat /opt/user.txt)
for UNAME in $ULIST
do
useradd $UNAME
echo "123456"|passwd --stdin $UNAME &> /dev/null
done
————————————————————
#!/bin/bash
#根据用户列表文件删除用户
ULIST=$(cat /opt/user.txt)
for UNAME in $ULIST
do
userdel -r $UNAME &> /dev/null
done
————————————————————
#!/bin/bash
#测试文件中的 IP 是否连通
IPLIST=$(cat /opt/ipaddrs.txt)
for IP in $IPLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null
if [ $? -eq 0 ];then
echo "Host $IP is up"
else
echo "Host $IP is down"
fi
done
————————————————————
#!/bin/bash
#测试 IP 网段
for i in {
1..255}
do
ping -c 2 -i 0.1 -W 1 192.168.10.$i &>/dev/dull
if [ $? -eq 0 ]
then
echo "192.168.10.$i is up"
else
echo "192.168.10.$i is down"
fi
done
————————————————————
#!/bin/bash
#密码登录与确认
init=123456
for i in {
1..3}
do
read -p "请输入免密:" pass
if [ $pass == $init ];then
echo "密码正确"
exit
fi
done
echo "密码错误"
————————————————————
#!/bin/bash
#九九乘法表一
for i in {
1..9};do
for j in `seq $i`;do
echo -e "${j}x${i}=$[i*j]tc"
done
echo
done
————————————————————
#!/bin/bash
#九九乘法表二
for ((i=1;i<10;i ));do
for((j=1;j<=i;j ));do
echo -e "${j}x${i}=$[i*j]tc"
done
echo
done
————————————————————
#!/bin/bash
#生成菱形
read -p "请输入菱形行数:" RM
for ((i=1;i<=$RM;i ))
do
for ((j=$RM;j>$i;j--))
do
echo -n " "
done
for ((n=1;n<=$i;n ))
do
echo -n "* "
done
echo ""
done
for ((k=$RM-1;k>=1;k--))
do
for ((m=$RM;m>$k;m--))
do
echo -n " "
done
for ((l=1;l<=$k;l ))
do
echo -n "* "
done
echo ""
done
(4) while 循环
格式:
代码语言:javascript复制while CONDITION;
do
循环体
done
说明: CONDITION:循环控制条件,进入循环之前,先做一次判断,每一次循环之后会再次做判断。条件为 “true” 则执行一次循环,直到条件测试状态为 “false” 终止循环,因此:CONDTION 一般应该有循环控制变量,而此变量的值会在循环体不断地被修正。 进入条件:CONDITION 为 true 退出条件:CONDITION 为 false
示例:
代码语言:javascript复制#!/bin/bash
i=1
while [ $i -le 5 -a $i -ge 0 ]
do
echo $i
let i
done
echo -e "