shell脚本
解释器Shell
-
Shell是Linux内核与用户之间的解释器程序,负责向内核翻译及传达用户或程序的指令。
-
Shell解释器通常默认是指bash解释器
/bin/bash。/etc/shells:存储系统中解释器的路径。- bash解释器支持:Tab键、快捷键、历史命令、别名、管道、重定向、标准输入输出。
-
解释器的使用方式:
- **交互式:**需要人工干预,执行效率低。
- **非交互式:**安静地在后台执行,执行效率高,方便写脚本。
-
不同解释器执行ls命令:
-
使用sh解释器之前:账户登录(root)——> bash解释器——> ls命令:bash解释器是有颜色的。root默认通过bash解释器执行ls命令。
-
使用sh解释器之后:账户登录(root)——> bash解释器——> sh命令——>sh解释器——> ls命令:sh解释器是黑白的。root默认通过bash解释器来执行sh命令,sh命令进入sh解释器,通过sh解释器执行ls命令。
-
-
usermod -s 解释器 用户名:更改该用户的登录解释器。
// 切换解释器
]# cat /etc/shells // 查看系统中的解释器
/bin/sh // sh的快捷方式,多数Unix的默认解释器
/bin/bash // bash的快捷方式,多数Linux的默认解释器
/usr/bin/sh // sh的快捷方式
/usr/bin/bash // bash的快捷方式
]# ls -l /bin/{sh,bash} /usr/bin/{sh,bash}
-rwxr-xr-x. 1 root root 1150568 4月 12 2022 /bin/bash
lrwxrwxrwx. 1 root root 4 4月 12 2022 /bin/sh -> bash // 在该设备中,/bin/sh是bash的快捷方式
-rwxr-xr-x. 1 root root 1150568 4月 12 2022 /usr/bin/bash
lrwxrwxrwx. 1 root root 4 4月 12 2022 /usr/bin/sh -> bash
]# sh // 使用sh解释器
sh-4.2# ls // 返回内容黑白
sh-4.2# exit
]# yum -y install ksh // 安装ksh解释器
]# ksh // 使用ksh解释器,ksh解释器不支持快捷键、tab、方向键
# ls // 返回内容黑白
shell脚本与重定向输出
shell 脚本
-
脚本是提前写好可执行语句,能够完成特定任务的文件。shell脚本是通过bash解释器运行的脚本。
-
编写shell脚本(见名知意,扩展名用
.sh):- 声明解释器:
#!/bin/bash。 - 注释:
#注释内容,注释内容有脚本功能、作者信息、变量作用等等。 - 命令序列:系统依次执行若干条命令(非交互式命令)。
- 声明解释器:
-
执行shell脚本:
- 方式1:赋予脚本执行权限
x:chmod +x 脚本路径,后直接运行:脚本路径。 - 方式2:以子进程bash解释器执行脚本(首选):
bash 脚本路径。- bash命令会新建一个bash的子进程,后台静默执行脚本,bash子进程执行完脚本后会自杀:
账户登录(root)——> bash解释器——> bash命令——> bash子进程——> 脚本
- bash命令会新建一个bash的子进程,后台静默执行脚本,bash子进程执行完脚本后会自杀:
- 方式3:以当前的解释器执行该脚本:
source 脚本路径,也可以写为. 脚本路径。- 账户登录(root)——> bash解释器——> 脚本。
- 方式1:赋予脚本执行权限
-
bash -x 脚本路径:调试脚本,会显示脚本执行的每个步骤。
// 执行脚本文件的三种方式
~]# echo '
> #!/bin/bash // 声明解释器
> # This is a test shell. // 注释
> echo hello world
> mkdir /opt/abc
> cd /opt/abc
> touch xyz // 相对路径创建/opt/abc/xyz
> ' > /opt/test01.sh // 编写脚本/opt/test01.sh
// 方式1:赋予脚本执行权限后直接以脚本路径运行
~]# chmod +x /opt/test01.sh
~]# /opt/test01.sh
hello world
~]# ls /opt/abc
xyz // 成功执行
// 方式2:bash
~]# chmod -x /opt/test01.sh
~]# rm -rf /opt/abc
~]# bash /opt/test01.sh
hello world
~]# ls /opt/abc // 开一个子进程bash执行脚本后自杀,因此当前不进入/opt/abc
xyz // 成功执行
// 方式3:source
~]# rm -rf /opt/abc
~]# . /opt/test01.sh // 也可以写为"source /opt/test01.sh"
hello world
abc]# // 以当前的bash执行脚本,因此当前进入了/opt/abc
abc]# ls /opt/abc
xyz // 成功执行
脚本搭建YUM仓库
// 脚本搭建本地YUM仓库
]# cat /opt/yumrepo1.sh
rm -rf /etc/yum.repos.d
mkdir /etc/yum.repos.d
echo '
[app]
name=appstream
baseurl=file:///mydvd/AppStream
enabled=1
gpgcheck=0
#gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
[base]
name=baseos
baseurl=file:///mydvd/BaseOS
gpgcheck=0
' > /etc/yum.repos.d/mydvd.repo
mkdir /mydvd
echo '
/dev/sr0 /mydvd iso9660 defaults 0 0
' >> /etc/fstab
mount -a
]# bash /opt/yumrepo1.sh
// 脚本搭建网络YUM仓库
// 需要192.168.88.240开启ftp匿名访问,共享目录/var/ftp/dvd下存在YUM仓库,还需要处于同一个网段本机才能直接使用该网络YUM仓库
]# cat /opt/yumrepo2.sh
rm -rf /etc/yum.repos.d
mkdir /etc/yum.repos.d
echo '
[app]
name=appstream
baseurl=ftp://192.168.88.240/dvd/AppStream
gpgcheck=0
[base]
name=baseos
baseurl=ftp://192.168.88.240/dvd/BaseOS
gpgcheck=0
' > /etc/yum.repos.d/mydvd.repo
]# bash /opt/yumrepo2.sh
脚本搭建ftp服务
// 脚本搭建ftp服务
// 开启ftp服务,并将hosts共享文件放入ftp共享目录pub
虚拟机(192.168.166.2):
]# cat /opt/ftp.sh
yum install -y vsftpd
systemctl restart vsftpd.service
cp /etc/hosts /var/ftp/pub
]# bash /opt/ftp.sh
]# vim /etc/vsftpd/vsftpd.conf // 修改配置文件
anonymous_enable=YES // 修改该行
]# systemctl restart vsftpd.service
]# curl ftp://192.168.166.2
drwxr-xr-x 2 0 0 19 Jun 28 03:25 pub
重定向输出
- 重定向标准输出(
>、>>):只重定向标准输出。 - 重定向错误输出(
2>、2>>):只重定向错误输出。 - 重定向所有输出(
&>、&>>):重定向标准输出和错误输出。 /dev/null:该文件能无限容纳字符。
// 重定向:标准输出、错误输出、所有输出
]# ls /mnt/abc.txt
/mnt/abc.txt
]# ls /mnt/xyz.txt
ls: 无法访问'/mnt/xyz.txt': 没有那个文件或目录
]# ls /mnt/abc.txt > /opt/test.txt // 重定向标准输出
]# ls /mnt/xyz.txt 2> /opt/test.txt // 重定向错误输出
]# ls /mnt/abc.txt /mnt/xyz.txt > /opt/test.txt // 重定向标准输出
ls: 无法访问'/mnt/xyz.txt': 没有那个文件或目录 // 错误输出
]# ls /mnt/abc.txt /mnt/xyz.txt 2> /opt/test.txt // 重定向错误输出
/mnt/abc.txt // 标准输出
]# ls /mnt/abc.txt /mnt/xyz.txt &> /opt/test.txt // 重定向所有输出。不返回输出语句
]# yum install -y vsftpd &> /dev/null // 重定向所有输出。不返回输出语句
脚本搭建Web服务
// 脚本搭建网站服务
// 开启服务并设置开机自启,并定义默认页面内容为"httpd-test~~~",脚本执行过程要静默
虚拟机(192.168.166.2):
]# cat /opt/httpd.sh
#!/bin/bash
yum install -y httpd > /dev/null
systemctl restart httpd > /dev/null
systemctl enable httpd > /dev/null
echo 'httpd-test~~~' > /var/www/html/index.html
]# bash /opt/httpd.sh
]# curl 192.168.166.2
httpd-test~~~
]# systemctl is-enabled httpd
enabled
变量与定界符
变量与常量
-
变量(variable):以固定的名称存放可能会变化的内容。
-
定义变量:
变量名=变量值。- 等号两边不能有空格。
- 变量名只能由字母、数字、下划线组成,区分大小写。变量名不能以数字开头。
- 定义变量是临时的,定义在脚本中只在脚本内有效,定义在终端只在当前解释器进程有效(在新终端失效,在子进程解释器也失效)。
- 定义永久变量:在配置文件
/etc/profile定义变量,重启机器或者source /etc/profile生效。
-
调用变量:
$变量名或${变量名}。 -
取消变量:
unset 变量名或变量名=。
-
-
常量(constant):固定不变的内容。
// 变量
]# a=10 // 定义变量并赋值
]# echo $a
10
]# a=100 // 重新赋值
]# echo $a
100
]# echo $aRMB.abc // 没有定义变量aRMB,变量aRMB值为空。因为变量名you
.abc
]# echo $a.RMB // 变量a为100
100.RMB
]# echo ${a}RMB // 变量a为100
100RMB
]# a= // 重新赋值为空(没有空格),相当于"unset a"
环境变量
- 环境变量:系统定义的变量。
USER:当前用户名。
UID:当前用户UID。
HOME:当前用户家目录。
SHELL:当前用户解释器。
PWD:当前位置。
HOSTNAME:主机名。
LOGNAME:登录的用户名。RANDOM:产生0到几万的随机整数。PS1(Prompt String):一级提示符。PS2:二级提示符,使用\强制换行时出现二级提示符>。PATH:存储系统命令的路径。系统依靠这里的路径来查找使用命令功能。TZ:时区。空则默认为零时区。
env(environment):显示所有环境变量。set:显示所有变量。
// 一级提示符
[root@hostname /]# echo $PS1 // 查看默认的一级提示符
[\u@\h \W]\$
[root@hostname /]# PS1='hehe#' // 修改一级提示符
hehe# // 显示效果
hehe#PS1='[\u@\h \W]\$ ' // 恢复原有设置
[root@hostname /]#
// 二级提示符
]# echo $PS2 // 查看默认的二级提示符
>
]# ls \ // "\"强制换行,观察提示符"> "
> -ld \ // "\"强制换行
> /opt
drwxr-xr-x. 3 root root 85 6月 28 11:59 /opt
]# PS2='=> ' // 修改二级提示符为"=> "
]# ls \ // "\"强制换行,观察提示符
=> -ld \
=> /opt
drwxr-xr-x. 3 root root 85 6月 28 11:59 /opt
]# PS2='> ' // 恢复原有设置
// 环境变量PATH
]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin // 系统命令的路径
]# PATH=
bash: sed: 没有那个目录
]# ls // 系统根据PATH的路径来寻找系统命令,现在找不到ls命令
bash: ls: 没有那个文件或目录
bash: sed: 没有那个目录
位置变量与预定义变量
-
$1、$2、$3、……:位置变量,代表脚本后面的第1、2、3、……个参数。 -
$*:所有位置变量的值。$*:将所有参数作为单个字符串返回,并使用第一个IFS(Internal Field Separator)变量的值作为参数之间的分隔符。通常情况下,IFS的默认值是空格字符。因此,$*展开为一个单独的字符串,参数之间用空格分隔。$@:将每个参数作为独立的字符串返回,并保留参数之间的空格。当使用$@时,每个参数都被视为一个独立的元素。
-
$#:位置变量的个数。 -
$0:当前运行的进程名或脚本名。 -
$?:上一个命令的执行结果(退出码)。- 执行成功为0,执行失败为非0。
- 查看退出码的实际含义:
perror 数字
-
$$:当前运行的进程的PID。 -
$!:后台最后一个进程的PID。
// 位置变量与预定义变量
~]# vim /opt/test05.sh
#!/bin/bash
echo $0 // 脚本的名称(当前位置),source运行脚本显示解释器的名字
echo $1 // 第一个参数
echo $2 // 第二个参数
echo $* // 所有的参数
echo $# // 所有参数的个数
echo $$ // 当前进程的进程号PID
echo $? // 上一个程序执行结果,成功为0,失败非0
~]# bash /opt/test05.sh a b c // 绝对路径执行脚本
/opt/test05.sh
a
b
a b c
3
38544
0 // 0代表上一个命令"echo $$"执行成功
~]# cd /opt
opt]# bash test05.sh a b c // 相对路径执行脚本
test05.sh
a
b
a b c
3
38553
0
opt]# vim test06.sh
#!/bin/bash
echo $0
ls sdfaasdf
echo $?
opt]# source test06.sh
bash // source运行脚本时,$0是解释器进程名称
ls: 无法访问'sdfaasdf': 没有那个文件或目录
2 // 非0代表上一个命令"ls sdfaasdf"执行失败
// 应用:创建用户并设置密码
]# vim /opt/useradd.sh
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
]# bash /opt/useradd.sh zhangsan 123
更改用户 zhangsan 的密码 。
passwd:所有的身份验证令牌已经成功更新。
定界符 “”、‘’、$()
- 三种定界符:
- 双引号 " ":其内为整体,允许扩展,其内可以使用
$引用变量。echo后省略引号默认是双引号。 - 单引号 ’ ':其内为整体,禁用扩展,不支持变量和正则,其内使用
$视为普通字符。 - 反撇号 ``,也可以写作**$():可以将命令的输出内容**作为变量值。
- 例如:
rpm -e $(rpm -qa | grep mariadb):卸载包含mariadb字符串的软件。 - 例如:
rpm -ql mysql > /opt/1.txt后rm -rf $(cat /opt/1.txt):删除mysql相关的文件。
- 例如:
- 双引号 " ":其内为整体,允许扩展,其内可以使用
// 引号的作用:其内为整体
]# cd /mnt
mnt]# touch a b // 创建两个文件
mnt]# ls -l
总用量 0
-rw-r--r--. 1 root root 0 6月 28 16:42 a
-rw-r--r--. 1 root root 0 6月 28 16:42 b
mnt]# touch "a b" // 创建一个文件,这里也可以使用单引号'a b'
mnt]# ls -l
总用量 0
-rw-r--r--. 1 root root 0 6月 28 16:42 a
-rw-r--r--. 1 root root 0 6月 28 16:42 'a b'
-rw-r--r--. 1 root root 0 6月 28 16:42 b
]# xx=a b c
-bash: b: command not found // 未界定时赋值失败
]# xx="a b c" // 赋值成功,这里也可以写作 xx='a b c'
// 双引号和单引号的区别
]# echo "$xx" // 双引号内允许扩展,可以使用$引用变量
a b c
]# echo '$xx' // 单引号内禁用扩展,$是普通字符
$xx
// 反撇号的作用
]# date
2023年 06月 28日 星期三 16:26:35 CST
]# a=date // 将date看成字符串
]# echo $a
date
]# a=`date` // 可以保留date命令的功能,也可以写作 a=$(date)
]# echo $a
2023年 06月 28日 星期三 16:26:35 CST
// 应用:创建一个文件,名称"abc-年-月-日.txt"
]# touch /mnt/abc-`date +%Y-%m-%d`.txt
]# ls /mnt
a 'a b' abc-2023-06-28.txt b
交互式赋值 read
read [-p "提示信息"] 变量名1 变量名n(prompt):从键盘读入变量值完成赋值(交互式)。- 可以给多个变量赋值,以空格作为区分。
stty -echo(set tty):屏蔽回显。(但不会屏蔽提示信息)
stty echo:恢复回显。
// read,交互式创建用户并设置密码
]# vim /opt/read.sh
#!/bin/bash
read -p "please input user's name:" x
useradd $x
stty -echo // 屏蔽回显,不会屏蔽提示信息
read -p "please input password:" y
stty echo // 恢复回显
echo ''
echo $y | passwd --stdin $x
]# bash /opt/read.sh // 运行脚本
please input user's name:lisi
please input password: // 屏蔽密码的显示
更改用户 lisi 的密码 。
passwd:所有的身份验证令牌已经成功更新。
局部变量与全局变量 export
- 局部变量:定义的变量默认仅在当前Shell环境中有效,无法在子Shell环境中使用。
- 全局变量:在当前Shell及子Shell环境中均有效(向下生效),但无法在父Shell环境中使用。
export 变量名1[=变量值1] 变量名n[=变量值n]:可以将局部变量声明为全局变量,也可以直接创建全局变量。
export -n 全局变量名1 全局变量名n:取消指定变量的全局属性。
// 局部变量与全局变量
]# pstree | grep bash // 查看bash进程
|-sshd---sshd---sshd---bash-+-grep // 处于第一层的bash
]# a=101 // 第一层创建局部变量a,赋值为101
]# echo $a
101
]# bash // 创建子进程bash并进入
]# pstree | grep bash
|-sshd---sshd---sshd---bash---bash-+-grep // 处于第二层的bash
]# b=202 // 第二层创建局部变量b,赋值为202
]# echo "$a+$b"
+202
]# export b // 将第二层局部变量b声明为全局变量(向下生效)
]# export B=200 // 第二层创建全局变量B(向下生效),赋值为200
]# echo "$a+$b+$B" // 第一层的局部变量a在第二层不存在
+202+200
]# bash
]# pstree | grep bash
|-sshd---sshd---sshd---bash---bash---bash-+-grep // 处于第三层的bash
]# c=303 // 第三层创建局部变量c,赋值为303
]# echo "$a+$b+$B+$c"
+202+200+303 // 第一层的局部变量a在第三层不存在,第二层的全局变量b和B在第三层存在
]# exit // 退出到第二层
exit
]# pstree | grep bash
|-sshd---sshd---sshd---bash---bash-+-grep // 处于第二层的bash
]# echo "$a+$b+$B+$c"
+202+200+ // 子进程的变量在父进程不存在:第三层的变量c在第二层不存在
]# exit // 退出到第一层
exit
]# pstree | grep bash
|-sshd---sshd---sshd---bash-+-grep // 处于第一层的bash
]# echo "$a+$b+$B+$c"
101+++ // 子进程的变量在父进程不存在:第二层的变量b和B在第一层不存在
shift命令
shift 是一个 Bash 命令,用于将脚本或函数的参数列表中的参数向左移动。当你调用一个函数或脚本时,传入的参数会被存储在一个参数列表中,通过 $1、$2、$3 等方式进行访问。
当你使用 shift 命令时,第一个参数 $1 会被丢弃,而原本的 $2 被移动到 $1 的位置,原本的 $3 被移动到 $2 的位置,以此类推。这意味着所有的参数都向左移动一位。
]# cat test_shift.sh
#!/bin/bash
echo "The total number of arguments is: $#"
echo "First argument is: $1"
echo "Second argument is: $2"
echo "Third argument is: $3"
shift
echo "After shifting"
echo "First argument is now: $1"
echo "Second argument is now: $2"
echo "Third argument is now: $3"
]# bash ./test_shift.sh apple banana cherry
The total number of arguments is: 3
First argument is: apple
Second argument is: banana
Third argument is: cherry
After shifting
First argument is now: banana
Second argument is now: cherry
Third argument is now:
运算
- 运算符:
+、-、*、/取整、%取余。
整数运算 expr、$[]、let
expr 整数1 运算符1 整数2 运算符n 整数n(expression):运算并输出值。- 运算符两侧必须空格,特殊符号使用引号或转义字符,引用变量必须
$。
- 运算符两侧必须空格,特殊符号使用引号或转义字符,引用变量必须
$[算术运算]或$((算术运算)):运算但不输出值。- 运算符两侧可以不空格,特殊符号直接使用,引用变量可以不
$。 - 注意:
$()是反撇号,$[]是运算,${}是变量。
- 运算符两侧可以不空格,特殊符号直接使用,引用变量可以不
let 变量名算术运算:运算但不输出值。可以直接用运算给变量赋值;支持变量自运算。- 自运算:
++、--、+=、-=、*=、/=、%=。
- 自运算:
// 整数运算
// expr
]# expr 6 + 4
10
]# expr 6 - 4
2
]# expr 6 '*' 4 // 通过引号使用特殊符号*本身
24
]# expr 6 \* 4 // 也可以通过转义字符\使用特殊符号
24
]# expr 6 / 4 // 除法取整
1
]# expr 6 % 4 // 除法取余
2
]# a=6
]# b=4
]# expr $a \* $b // 引用变量必须$
24
// $[]、$(())
]# echo $[6*4] // 直接使用特殊符号*
24
]# echo $((6*4))
24
]# echo $[$a*$b]
24
]# echo $[a*b] // 引用变量可以不$
24
]# x=$[6*4] // 不直接输出值,可以给变量赋值
]# echo $x
24
]# echo $[RANDOM] // 随机返回一个非0整数
9594
// let
]# y=5+9 // 字符串给变量赋值
]# echo $y
5+9
]# let y=5+9 // 用运算给变量赋值
]# echo $y
14
]# let y++ // 支持变量自增减
]# echo $y
15
小数运算 bc
echo "1.1+1" | bc:返回2.1。echo "10/3" | bc:返回3。
echo "scale=3;5/3" | bc:截取小数点后三位,返回1.666,多个表达式用;隔开。- 可以直接使用命令
bc进入交互式运算界面,quit退出,scale=n约束小数位。
// 小数运算
// bc交互式运算
]# bc
12.34*56.789
700.776 // 根据两位小数与三位小数运算来截取三位小数,注意不是四舍五入
scale=4 // 结果截取四位小数
12.34*56.789
700.7762
3*1
3.0000
quit
// bc非交互式
]# A=12.34
]# echo "$A*56.789;5/3" | bc
700.776
1
]# echo "scale=4;$A*56.789;5/3" | bc
700.7762
1.6666
字符串拼接
变量名1=$变量名2$变量名3:将变量2和变量3的字符串合并后赋予给变量1。例如:a=$m$n变量名1+=$变量名2:将变量1和变量2的字符串合并后赋予给变量1。例如:a+=$m
// 字符串拼接
]# m=1
]# n=2
]# l=$m$n // 方式1
]# echo $l
12
]# l+=$m // 方式2
]# echo $l
121
数值比较
- 比较符:
>、>=、<、<=、==、!=。 $[整数1 比较符 整数2]:true为1,false为0,比较符两侧可不空格。只能比较整数,不输出内容。echo "数字1 比较符 数字2" | bc:true为1,false为0,比较符两侧可不空格。
// 数值比较
]# echo $[1<2]
1
]# echo $[1.1<2]
-bash: 1.1<2: 语法错误: 无效的算术运算符 (错误符号是 ".1<2")
]# echo "1.1<2" | bc
1
]# echo "1.10==1.1" | bc
1
条件测试 []
- 条件测试的书写格式(测试内容也叫表达式):
test 表达式或[ 表达式 ],不输出内容。- 注意:方括号与表达式之间要有空格。
字符串比较
test 表达式或[ 表达式 ],不输出内容。[ 字符串1 == 字符串2 ]:比较两个字符串是否相等,相等则true。[ 字符串1 != 字符串2 ]:比较两个字符串是否不相等,不相等则true。[ -z 调用变量 ]:判断该变量是否为空,空则true。[ ! -z 调用变量 ]:判断该变量是否为非空,非空则true。[ -n "调用变量" ]:判断该变量是否为非空,非空则true。注意需要使用双引号。
- 注意:当调用变量为空时会报错,可以使用双引号
"调用变量",这样调用的变量为空时也不会报错。
// 字符串比较
// ==与!=
]# test a == a // 也可以写为[ a == a ],注意空格
]# echo $?
0 // 0为true
]# test a == b
]# echo $?
1 // 非0为flase
]# [ a != a ]
]# echo $?
1 // 非0为flase
]# [ a != b ]
]# echo $?
0 // 0为true
// 报错:"调用变量",使用双引号表示有输入内容,当调用的变量为空时也不会报语法错误。
]# m=abc
]# n= // 没有空格为空
]# [ $m == $n ] // 变量n为空报错
-bash: [: abc: 需要一元表达式
]# [ $m == "$n" ] // 变量n为空不报错
]# echo $?
1
// -z与! -z
]# [ -z $n ] // 变量n为空则true
]# echo $?
0
]# [ ! -z $n ] // 也可以写为[ -n "$n" ]
]# echo $?
1
逻辑与或
&&:逻辑与,使用同Java的短路与,前者成功才执行后者,全真整体为真。||:逻辑或,使用同Java的短路或,前者失败才执行后者,一真整体为真。- 组合:
A && B && C:A && B的结果来逻辑与CA || B && C:A || B的结果来逻辑与CA && B || C:A && B的结果来逻辑或CA || B || C:A || B的结果来逻辑或C
// &&与||的组合
mnt]# touch a c // 创建文件用"ls 文件"查看,创建目录用"ls -d 目录"查看
mnt]# ls
a c
mnt]# ls a && ls b && ls c // a成功后继续执行b,b失败,a&&b失败,逻辑与不执行c
a
ls: 无法访问'b': 没有那个文件或目录
mnt]# ls a && ls b || ls c // a成功后继续执行b,b失败,a&&b失败,逻辑或执行c
a
ls: 无法访问'b': 没有那个文件或目录
c
mnt]# ls a || ls b && ls c // a成功后不执行b,a||b成功,逻辑与执行c
a
c
mnt]# ls a || ls b || ls c // a成功后不执行b,a||b成功,逻辑或不执行c
a
// 应用:非root用户运行脚本就退出
]# vim /etc/yuhuo.sh
#!/bin/bash
[ $USER != root ] && echo "unsuccessfully" && exit // 用户非root就输出语句,前两者均成功后退出
echo "execute successfully"
]# bash /etc/yuhuo.sh
execute successfully
]# su - zhangsan
]$ bash /etc/yuhuo.sh
unsuccessfully
整数值比较
[ 整数1 操作符 整数2 ],不输出内容。- 操作符:
-
-eq(equal):等于。 -
-ne(not equal):不等于。 -
-ge(greater equal):大于等于。 -
-le(lesser equal):小于等于。 -
-gt(greater than):大于。 -
-lt(lesser than):小于。
-
- 操作符:
// 整数值比较
]# X=100
]# [ $X -eq 100 ] && echo yes || echo no // 等于
yes
]# [ $X -ne 100 ] && echo yes || echo no // 不等于
no
]# [ $X -gt 100 ] && echo yes || echo no // 大于
no
]# [ $X -ge 100 ] && echo yes || echo no // 大于等于
yes
]# [ $X -le 100 ] && echo yes || echo no // 小于等于
yes
]# [ $X -lt 100 ] && echo yes || echo no // 小于
no
// 应用:运行脚本必须输入两个位置参数
]# vim /opt/useradd.sh
#!/bin/bash
[ $# -ne 2 ] && echo "必须有两个位置参数:用户 密码" && exit
// 也可以写为 [ $# != 2 ] && echo "必须有两个位置参数:用户 密码" && exit
useradd $1
echo $2 | passwd --stdin $1
]# bash /opt/useradd.sh test
必须有两个位置参数:用户 密码
]# id test
id: “test”:无此用户
// 应用:编写脚本,root每2分钟检查服务器的用户数量,如果数量增加就给root发报警邮件
]# cat /etc/passwd | wc-l
48
]# vim /opt/checkuser.sh
#!/bin/bash
a=`cat /etc/passwd | wc-l` // 反撇号写法1
b=$(echo "server invaded" | mail -s warning root) // 反撇号写法2
[ &a -gt 48 ] && b
]# echo "*/2 * * * * bash /opt/checkuser.sh" > /var/spool/cron/root // 给root添加计划任务
// 也可以"crontab -e -u root",当前用户是root时可以直接"crontab -e"
// 停止计划任务
]# crontab -r -u root // 当前用户是root时可以直接"crontab -r"
// 也可以"echo > /var/spool/cron/root"
// 清空邮箱
]# echo > /var/spool/mail/root
文件状态测试
[ 操作符 目标 ],目标可以是文件或目录,不输出内容。如果目标是变量,则"${变量名}"- 操作符:
-e(exist):判断目标是否存在。! -e不存在。-d(directory):判断目标是否是目录。-f(file):判断目标是否是文件。-r(read):判断当前用户对目标是否有可读权限r。对root无效。-w(write):判断当前用户对目标是否有可写权限w。对root无效。-x(execute):判断当前用户对目标是否有可执行权限x。
- 操作符:
- 注意:root对一切目录和文件有rw权限。
// 文件状态测试
[root@166-2 ~]# chmod 044 /opt/test01.sh
[root@166-2 ~]# ls -l /opt/test01.sh
----r--r--. 1 root root 93 6月 28 10:28 /opt/test01.sh
[root@166-2 ~]# [ -e /opt/test01.sh ] && echo yes || echo no // 是否存在
yes
[root@166-2 ~]# [ -d /opt/test01.sh ] && echo yes || echo no // 是否是目录
no
[root@166-2 ~]# [ -f /opt/test01.sh ] && echo yes || echo no // 是否是文件
yes
[root@166-2 ~]# [ -r /opt/test01.sh ] && echo yes || echo no // 是否可读,对root无效
yes
[root@166-2 ~]# [ -w /opt/test01.sh ] && echo yes || echo no // 是否可写,对root无效
yes
[root@166-2 ~]# [ -x /opt/test01.sh ] && echo yes || echo no // 是否可执行
no
[root@166-2 ~]# su - zhangsan
[zhangsan@166-2 ~]$ [ -r /opt/test01.sh ] && echo yes || echo no // 是否可读
yes
[zhangsan@166-2 ~]$ [ -w /opt/test01.sh ] && echo yes || echo no // 是否可写
no
[zhangsan@166-2 ~]$ [ -x /opt/test01.sh ] && echo yes || echo no // 是否可执行
no
[[ ]]
-
[[]]和[]都是 Bash 中的条件判断结构,但它们之间有一些区别。- 语法:
[[]]是 Bash 特有的条件判断结构,而[]则是 POSIX 标准的条件判断结构,也被称为 test 命令。 - 字符串处理能力:
[[]]结构支持更多的字符串处理能力,如正则表达式匹配,字符串拓展等。 - 高级逻辑运算符:
[[]]结构支持高级的逻辑运算符,例如&&和||,可以进行复杂的逻辑判断。而[]结构中需要使用-a(与)、-o(或)等选项来完成逻辑操作。 - 引用变量:在
[[ ]]结构中,变量引用不需要使用双引号,即"$variable"可以简化为$variable。而在[]结构中,推荐对变量引用加上双引号,即"$variable"。 - 转义字符处理:
[[]]结构能够自动处理转义字符,而[]结构中需要对反斜杠进行转义,例如\[。
综上所述,
[[]]结构通常比[]结构更强大和更易用,因此在大部分情况下,建议使用[[ ... ]]进行条件判断。但需要注意的是,[[]]结构只能在 Bash 环境中使用,不适用于其他 POSIX 兼容的 shell。 - 语法:
-
[[ 字符串1 =~ 字符串2 ]]:比较字符串1是否匹配正则表达式字符串2,不匹配则true。
if选择结构 fi
if单分支结构
- 书写格式:(没写
fi会报错:语法错误:未预期的文件结尾)- 第一行:
if 条件测试,注意是条件测试不是条件。
第二行:then 命令序列(then可以单独写一行)
最后一行:fi - 第一行:
if 条件测试;then
第二行:命令序列
最后一行:fi - 命令序列:依次执行的若干条命令。
- 第一行:
if双分支结构
- 书写格式:
- 第一行:
if 条件测试
第二行:then 命令序列(then可以单独写一行)
第三行:else 命令序列(else可以单独写一行)
最后一行:fi - 第一行:
if 条件测试;then
第二行:命令序列
第三行:else 命令序列(else可以单独写一行)
最后一行:fi
- 第一行:
// if选择结构
[root@166-2 ~]# vim /opt/ifelse.sh
#!/bin/bash
if [ $UID == 0 ] // 可以写为if [ $UID == 0 ];then
then echo "I am administrator" // then可以单独写一行
echo ok
else echo "I am not administrator" // else可以单独写一行
echo no
fi
[root@166-2 ~]# bash /opt/ifelse.sh
I am administrator
ok
[root@166-2 ~]# su - zhangsan
[zhangsan@166-2 ~]$ bash /opt/ifelse.sh
I am not administrator
no
- ping命令选项:
-c(count):ping的次数。-i(interval):每次ping间隔几秒。-W:等待反馈的超时秒数,即ping不通后几秒返回信息。
// ping
]# ping -c 3 -i 0.2 -W 1 192.168.88.11 // ping3次,每次间隔0.2秒,ping不通1秒后返回信息
PING 192.168.88.11 (192.168.88.11) 56(84) bytes of data.
--- 192.168.88.11 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 410ms
// 脚本
]# cat /opt/ping.sh
#!/bin/bash
read -p "please input destination ip address:" ip
ping -c 3 -i 0.2 -W 1 $ip &> /dev/null
if [ $? == 0 ]
then
echo 'successfully'
else
echo 'unsuccessfully'
fi
]# bash /opt/ping.sh
please input destination ip address:192.168.88.11
unsuccessfully
if多分支结构
- 书写格式:
- 第一行:
if 条件测试1
第二行:then 命令序列1
第三行:elif 条件测试2
第四行:then 命令序列2
第五行:elif 条件测试n
第六行:then 命令序列n
第七行:else 命令序列*
最后一行:fi
- 第一行:
// if多分支结构
// 根据输入的分数判断等级
# vim /opt/test04.sh
#!/bin/bash
read -p "exam score:" x
if [ $x -ge 90 ] && [ $x -le 100 ] ;then
echo 'A'
elif [ $x -ge 80 ] && [ $x -lt 90 ];then
echo 'B'
elif [ $x -ge 60 ] && [ $x -lt 80 ];then
echo 'C'
elif [ $x -ge 0 ] && [ $x -lt 60 ];then
echo 'D'
else
echo 'Error.Please input a number between 0 and 100'
fi
]# bash /opt/test04.sh
exam score:90
A
]# bash /opt/test04.sh
exam score:77
C
]# bash /opt/test04.sh
exam score:111
Error.Please input a number between 0 and 100
循环结构 for、while
for循环
- 书写格式:
- 第一行:
for 变量名 in 变量值列表
第二行:do
第三行:命令序列
最后一行:done - 变量值列表:
- 情况1,直接列出每个变量:
for i in a b c,也可以写为for i in {a,b,c}。 - 情况2,连续整数:
for i in {1..4},相当于for i in {1,2,3,4}。 - 情况3,调用变量:
for i in $(seq $a)。seq 尾数。seq 首数 尾数。seq 首数 增量 尾数。
- 情况4,调用文本文件的内容:
for i in $(cat 文本文件路径)。每一行内容为一个变量值。
- 情况1,直接列出每个变量:
- 第一行:
// for循环
// 情况1:变量值列表,单个列出
]# vim /opt/for01.sh
#!/bin/bash
for i in a b c // 也可以写为"for i in {a,b,c}"
do
echo "abc$i"
done
]# bash /opt/for01.sh
abca
abcb
abcc
// 情况2:变量值列表,连续整数
]# vim /opt/for02.sh
#!/bin/bash
for i in {1..3}
do
echo "abc$i"
done
]# bash /opt/for02.sh
abc1
abc2
abc3
// 情况3,变量值列表,调用变量
]# vim /opt/for03.sh
#!/bin/bash
a=3
for i in {1..$a} // {数1..数n},其内不支持变量
do
echo "abc$i"
done
]# bash /opt/for03.sh
abc{1..3}
]# vim /opt/for03.sh
#!/bin/bash
a=3
for i in `seq $a` // seq支持变量,也可以写为"for i in $(seq $a)"
do
echo "abc$i"
done
]# bash /opt/for03.sh
abc1
abc2
abc3
// 情况4,变量值列表,文本文件内容:脚本批量创建用户
]# vim /opt/name.txt
zhangsan
lisi
wangwu
zhaoliu
]# vim /opt/for04.sh
#!/bin/bash
for i in `cat /opt/name.txt` // 也可以写为"for i in $(cat /opt/name.txt)"
do
useradd $i
done
]# bash /opt/for04.sh
// 情况5,ping某个网段的IP地址
]# vim /opt/for05.sh
#!/bin/bash
s=0
u=0
for ip in {1..5}
do
ping -c 3 -i 0.2 -W 1 192.168.166.$ip &> /dev/null
if [ $? == 0 ];then // 上一条命令执行成功时
echo "ping 192.168.166.$ip successfully"
let s++
else
echo "ping 192.168.166.$ip unsuccessfully"
let u++
fi
done
echo "succeed $s times totally, fail $u times totally"
]# bash /opt/for05.sh
ping 192.168.166.1 successfully
ping 192.168.166.2 successfully
ping 192.168.166.3 successfully
ping 192.168.166.4 unsuccessfully
ping 192.168.166.5 unsuccessfully
succeed 3 times totally, fail 2 times totally
- 设置vim缩进
vim 用户家目录/.vimrc中au filetype sh set ai ts=4:自动在扩展名.sh的文件设置自动缩进为4个空格。
C语言风格的for循环
- 书写格式:
- 第一行:
for ((变量名=初始值;条件;步长控制)),这里的条件使用>=,不用条件测试的操作符-ge。
第二行:do
第三行:命令序列
最后一行:done
- 第一行:
// C语言风格的for循环
]# vim /opt/cfor01.sh
#!/bin/bash
for ((i=1;i<=5;i+=2))
do
echo abc$i
done
]# bash /opt/cfor01.sh
abc1
abc3
abc5
]# vim /opt/cfor02.sh
#!/bin/bash
i=0
for ((a=1;a<=5;a++))
do
let i=i+$a // 也可以写为"let i+=$a"
echo $i
done
]# bash /opt/cfor02.sh
1
3
6
10
15
while循环结构
- 条件格式:
- 第一行:
while 条件测试,注意是条件测试不是条件。
第二行:do
第三行:命令序列
最后一行:done - 说明:条件测试为真则执行命令序列,然后继续条件测试,直到条件测试为假,结束while循环。
- 第一行:
- for循环只用于有限次数的循环,while循环可用于无限次数的循环(死循环)。
- 死循环:条件测试恒为真,也可以写为
while :。 - 注意:死循环会占用大量的CPU(top命令动态查看%Cpu的空闲值id很低),给死循环的添加命令
sleep 0.1可以降低CPU占用。
- 死循环:条件测试恒为真,也可以写为
// while循环
// 死循环
]# vim /opt/while01.sh
#!/bin/bash
while [ 1 -eq 1 ] // 也可以写为"while :",这里的":"表示正确。
do
echo abc
sleep 0.1 // 可以在另一终端使用top查看进程的动态。没有sleep时死循环占用大量的CPU,%CPU的id值很低
done
]# vim /opt/while02.sh
#!adf/bin/bash
i=1
while [ $i -le 5 ]
do
echo abc$i
sleep 0.1
let i++
done
]# bash /opt/while02.sh
abc1
abc2
abc3
abc4
abc5
循环的中断与退出
break:结束所在循环结构。continue:结束本次循环。exit 0或1:0代表正常运行程序并退出程序,1代表非正常运行程序并退出程序,省略默认是0。不输出内容。- 将
exit 0或1写在末行,在程序退出后,用户可以echo $?来查看是0或1来判断程序是否正常退出。
- 将
// break、continue
]# vim /opt/end.sh
#!/bin/bash
for i in {1..4}
do
echo "第${i}次外部循环"
[ $i -eq 3 ] && continue // 当i=3时跳过本次循环,继续第4次外部循环
for j in {1..4}
do
echo "内部循环$j"
[ $j -eq 3 ] && break // 当j=3时跳出所在循环结构,不继续内部循环4
echo "正常结束内部循环$j"
done
echo "正常结束第${i}次外部循环"
echo ""
done
]# bash /opt/end.sh
第1次外部循环
内部循环1
正常结束内部循环1
内部循环2
正常结束内部循环2
内部循环3 // break没有"正常结束内部循环3",也没有开启"内部循环4"
正常结束第1次外部循环
第2次外部循环
内部循环1
正常结束内部循环1
内部循环2
正常结束内部循环2
内部循环3
正常结束第2次外部循环
第3次外部循环 // continue没有开启"内部循环1",但继续开启"第4次外部循环"
第4次外部循环
内部循环1
正常结束内部循环1
内部循环2
正常结束内部循环2
内部循环3
正常结束第4次外部循环
// 应用:写一个求和的脚本,每次输入一个参数,当用户输入0时结束脚本,没有输入时不报错。
]# vim /opt/qiuhe.sh
#!/bin/bash
i=0
while :
do
read -p "please input a number for their sum (input 0 to end):" n
[ -z $n ] && continue // 当n为空时跳过本次循环,继续下一次循环,也可以写为[ -n "$n" ] && continue
[ $n -eq 0 ] && break // 当n为0时结束所在循环结构,这里结束脚本
let i+=n
done
echo "their sum is $i"
]# bash /opt/qiuhe.sh
please input a number for their sum (input 0 to end): // 参数为空时继续下一次循环
please input a number for their sum (input 0 to end):1
please input a number for their sum (input 0 to end):23
please input a number for their sum (input 0 to end):561
please input a number for their sum (input 0 to end):231
please input a number for their sum (input 0 to end):485
please input a number for their sum (input 0 to end):0 // 参数为0时结束循环
their sum is 1301
case分支结构 esac
case分支结构
- 书写格式:
- 第一行:
case 调用的变量名 in
第二行:变量值1)
第三行:命令序列1;;
第四行:变量值2)
第五行:命令序列2;;
第六行:变量值n)
第七行:命令序列n;;
第八行:*)
第九行:命令序列;;
最后一行:esac - 说明:
- 当调用的变量成功匹配变量值1时,会执行命令序列1。
当调用的变量匹配变量值1失败时,会继续匹配变量值2,成功匹配则执行命令序列2,匹配失败则继续匹配下一个变量值。
所有设定的变量值均匹配失败后,会成功匹配通配符*(相当于 if选择结构的else),执行命令序列。 - 命令序列的最后一条命令以
;;结尾则跳转esac结束case分支,不以;;结尾则继续匹配下一个变量值(会报错)。
- 当调用的变量成功匹配变量值1时,会执行命令序列1。
- 第一行:
// case分支结构
]# vim /opt/case.sh
#!/bin/bash
case $1 in
abc)
echo ok;;
xyz)
echo no;;
*)
echo "please print $0 with abc or xyz"
esac
]# bash /opt/case.sh // 参数为空
please print /opt/case.sh with abc or xyz
]# bash /opt/case.sh aasdfa // 参数为aasdfa
please print /opt/case.sh with abc or xyz
]# bash /opt/case.sh abc // 参数为abc
ok
]# bash /opt/case.sh xyz // 参数为xyz
no
脚本安装源码包
// case分支结构应用:nginx软件不支持systmectl命令,nginx的命令路径太长,通过case分支结构来便捷使用nginx
// 安装软件nginx-1.22(源码包)
真机(192.168.166.1):
]# scp /linux-soft/s2/wk/lnmp_soft.tar.gz root@192.168.166.3: // 传输到root的家目录
虚拟机(192.168.166.3):
]# cp /root/lnmp_soft/nginx-1.22.1.tar.gz /opt
]# vim /opt/sourceinstall.sh // 安装源码包的脚本
#!/bin/bash
yum install -y gcc make openssl-devel pcre-devel > /dev/null
// 安装源码包需要gcc、make,安装nginx额外需要openssl-devel(支持搭建https加密网站)、pcre-devel(支持正则表达式),不显示标准输出" > /dev/null"
tar -xf nginx-1.22.1.tar.gz > /dev/null // 将该压缩包解压到当前目录
cd nginx-1.22.1 // 进入当前目录下的解压后的目录
./configure > /dev/null // 运行脚本文件"configure",以生成大纲文件"Makefile"
echo make******************** // 用以分割显示内容
make > /dev/null
echo makeinstall************* // 用以分割显示内容
make install > /dev/null
]# cd /opt
opt]# bash sourceinstall.sh
// 开启nginx服务以检测是否安装成功,nginx不支持systemctl命令
opt]# systemctl stop httpd // 关掉httpd服务,避免浏览器访问Web
opt]# systemctl stop firewalld // 关掉防火墙
opt]# less nginx-1.22.1/Makefile // 查看nginx软件主程序nginx的路径"/usr/local/nginx/sbin/nginx",或者通过查看README
opt]# /usr/local/nginx/sbin/nginx // 开启nginx,打开虚拟机的浏览器输入"192.168.166.3",看到nginx的欢迎页面
opt]# /usr/local/nginx/sbin/nginx // 重复开启会报错
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
opt]# /usr/local/nginx/sbin/nginx -s stop // 关闭nginx,打开虚拟机的浏览器输入"192.168.166.3",报错无法连接
opt]# /usr/local/nginx/sbin/nginx -s stop // 重复关闭会报错
nginx: [error] open() "/usr/local/nginx/logs/nginx.pid" failed (2: No such file or directory)
// 脚本使用nginx
]# vim /opt/usenginx.sh
#!/bin/bash
case $1 in
start|kai) // start或kai
/usr/local/nginx/sbin/nginx;;
stop|guan)
/usr/local/nginx/sbin/nginx -s stop;;
restart|cq)
/usr/local/nginx/sbin/nginx -s stop 2> /dev/null // 不显示重复关闭的错误输出
/usr/local/nginx/sbin/nginx;; // 命令序列的最后一条命令以";;"结尾
*)
echo "print start or stop or restart";;
esac
]# bash /opt/usenginx.sh start // 打开虚拟机的浏览器输入"192.168.166.3",看到nginx的欢迎页面
]# bash /opt/usenginx.sh stop // 打开虚拟机的浏览器输入"192.168.166.3",报错无法连接
]# bash /opt/usenginx.sh restart // 打开虚拟机的浏览器输入"192.168.166.3",看到nginx的欢迎页面
ss命令
- ss命令(Socket Statistics):程序正在运行会有一行信息。
ss 选项:查看联网的程序的信息(包括进程名、端口号、传输协议等)-n(numeric):以数字格式显示端口号。例如:不使用时显示http,使用时显示80。-t(tcp):显示TCP连接的端口。-u(udp):显示UDP连接的端口。-l(listening):显示所有服务正在监听的端口信息。例如:httpd启动后,会一直监听80端口。
-a(all):既显示服务端的端口,又显示客户端访问的临时端口。-p(processes):显示端口的服务名称,也就是程序名称。
// ss -ntulp
]# ss -ntulp | head -3
/* 同时使用选项-tu,才有"Netid"以区分tcp和udp。
* 不使用选项-l,"State"只显示ESTAB。
* 使用选项-p,"Process"才有内容。
*/
// 协议 运行状态 接收 发送 本地IP:端口号 可连接的IP:端口号 进程信息
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 0.0.0.0:35856 0.0.0.0:* users:(("avahi-daemon",pid=898,fd=17))
udp UNCONN 0 0 192.168.122.1:53 0.0.0.0:* users:(("dnsmasq",pid=1789,fd=5))
]# ss -tulp | head -3 // -n的作用:以数字格式显示端口号,注意"Port"
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 0.0.0.0:35856 0.0.0.0:* users:(("avahi-daemon",pid=898,fd=17))
udp UNCONN 0 0 192.168.122.1:domain 0.0.0.0:* users:(("dnsmasq",pid=1789,fd=5))
]# ss -nulp | head -3 // -t的作用:显示TCP连接的端口。以下都是UDP协议的
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
UNCONN 0 0 0.0.0.0:35856 0.0.0.0:* users:(("avahi-daemon",pid=898,fd=17))
UNCONN 0 0 192.168.122.1:53 0.0.0.0:* users:(("dnsmasq",pid=1789,fd=5))
]# ss -ntlp | head -3 // -u的作用:显示UDP连接的端口。以下都是TCP协议的
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:111 0.0.0.0:* users:(("rpcbind",pid=854,fd=4),("systemd",pid=1,fd=37))
LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3008,fd=8),("nginx",pid=3007,fd=8))
]# ss -ntup | head -3 // -l的作用:显示所有服务正在监听的端口信息。没有-l只显示已开启的服务,注意"State"的ESTAB
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp ESTAB 0 0 192.168.166.3:22 192.168.166.1:48804 users:(("sshd",pid=2258,fd=5),("sshd",pid=2225,fd=5))
]# ss -ntul | head -3 // -p的作用:显示端口的服务名称,也就是程序名称。注意"Process"
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 0.0.0.0:35856 0.0.0.0:*
udp UNCONN 0 0 192.168.122.1:53 0.0.0.0:*
// ss命令应用:查看nginx的运行状态
]# /usr/local/nginx/sbin/nginx -s stop
]# ss -ntulp | grep nginx // nginx处于关闭状态没有输出信息
]# echo $?
1
]# /usr/local/nginx/sbin/nginx
]# ss -ntulp | grep nginx // nginx处于运行状态有输出信息
tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=22560,fd=8),("nginx",pid=22559,fd=8))
]# echo $?
0
// 脚本:判断软件是否运行
]# vim /opt/isrun.sh
#!/bin/bash
ss -ntulp | grep -q $1 // 选项-q(quiet)不显示输出。
// 也可以写为 ss -ntulp | grep $1 > /dev/null
[ $? == 0 ] && echo "$1 is runnning." || echo "$1 is stopped."
// 也可以写为 [ $? -eq 0 ] && echo "$1 is runnning." || echo "$1 is stopped."
]# /usr/local/nginx/sbin/nginx -s stop
]# bash /opt/isrun.sh nginx
nginx is stopped.
]# /usr/local/nginx/sbin/nginx
]# bash /opt/isrun.sh nginx
nginx is runnning.
函数
- 在Shell环境中,可以将一些重复操作的命令序列,定义为公共的语句块,称为函数。
- 可以在终端定义函数,也可以在脚本定义函数。
- 函数的作用:避免代码重复,将大的工程分割为若干小的功能模块,代码可读性增强。
- 定义函数:
- 第一行:
function 函数名 {
第二行:命令序列
最后一行:} - 第一行:
函数名(){
第二行:命令序列
最后一行:}
- 第一行:
- 调用函数:
函数名 参数1 参数2 参数n。

// 颜色
// echo命令,选项-e使用某种功能,\033更改颜色,[数字m为颜色,[0m为默认色,30多是普通色,40多是背景色,90多是高亮色
]# echo -e "\033[31mABCD\033[0m" // 将颜色改为红色,输出ABCD,再将颜色改为默认色
ABCD // 显示为红色
]# echo -e "\033[32mABCD\033[0m"
ABCD // 显示为绿色
]# echo -e "\033[33mABCD\033[0m"
ABCD // 显示为黄色
]# echo -e "\033[34mABCD\033[0m"
ABCD // 显示为蓝色
]# echo -e "\033[35mABCD\033[0m"
ABCD // 显示为紫色
]# echo -e "\033[35mABCD" // 因为结尾没有"\033[0m"来将颜色还原为默认色,以下所有内容会变为紫色
ABCD
]# // 全部显示为紫色
]# echo -e "\033[0m"
// 函数应用1
]# vim /opt/color.sh
#!/bin/bash
color(){ // 定义函数color
echo -e "\033[$1m$2\033[0m" // $1为函数的参数1,$2为函数的参数2
}
color 31 red // 调用函数color,函数参数1为31,函数参数2为red
color 32 green
color 33 yellow
color 34 blue
color 35 purple
color 41 ABC41
color 42 ABC42
color 91 ABS91
color 92 ABC92
]# bash /opt/color.sh
red // 普通红色
green // 普通绿色
yellow // 普通黄色
blue // 普通蓝色
purple // 普通紫色
ABC41 // 红底
ABC42 // 绿底
ABS91 // 高亮红色
ABC92 // 高亮绿色
// 函数应用2
]# vim /opt/isrun.sh
#!/bin/bash
color(){
echo -e "\033[$1m$2\033[0m"
}
ss -ntulp | grep -q $1
[ $? == 0 ] && color 32 "$1 is runnning." || color 31 "$1 is stopped."
/* 脚本参数1如果在运行中输出绿字"$1 is runnning.",没有运行输出红字"$1 is stopped."
* 调用函数color,函数参数1是32,参数2是"$1 is runnning.",注意这里的$1是脚本参数。
*/
]# /usr/local/nginx/sbin/nginx -s stop
]# bash /opt/isrun.sh nginx
nginx is stopped. // 显示为红色
]# /usr/local/nginx/sbin/nginx
]# bash /opt/isrun.sh nginx
nginx is runnning. // 显示为绿色
退出关键字
- 三个退出的关键字:
exit 数字:退出所在脚本。定义脚本的$?,数字缺省为0。break:退出所在循环。return 数字:退出所在函数。定义函数的$?,数字缺省为0。- 注意:函数的输出语句使用
echo完成。
字符串处理 ${处理}
- 字符串处理不会改变变量本身的值,只是得到一串字符。没有输出内容。
字符串的截取 ${::}
${变量名:起始位置:长度}:从起始位置截取该长度的字符串。起始位置(索引号)从0起计。- 字符串截取不会改变变量本身的值,只是得到一串字符。
${#变量名}:返回变量的长度。
// 字符串截取
]# a=123456789
]# echo ${a:2:3} // 索引号从0起计,截取从第3位开始总共3个字符。
345
]# echo $a
123456789
// 应用1:随机取值
]# x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
]# echo ${x:61:1} // 验证索引号26+26+10-1=61的字符,大括号${变量}
9
]# n=$[RANDOM%62] // 环境变量RANDOM随机返回0到几万的正整数,中括号$[运算],${#x}返回变量x的长度
]# echo ${x:n:1} // 每次取一个字符,n随机取值[0,61]
// 应用2:随机生成8位密码的脚本
]# vim /opt/passgen.sh
#!/bin/bash
x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
pass= // 变量pass可以不赋予起始值,但使用source方式运行脚本时,会累加变量pass的字符串。
for i in {1..8}
do
n=$[RANDOM%62] // n随机取值[0,61]
p=${x:n:1} // 每次随机取x的一位字符
pass+=$p // 也可以写为"pass=$pass$p"
done
echo "password: $pass"
]# bash /opt/passgen.sh
password: nf81KqQ6
]# bash /opt/passgen.sh
password: zob8CO6G
字符串的替换 ${/ /}
${变量名/旧字符串/新字符串}:替换第一个旧字符串为新字符串。
${变量名//旧字符串/新字符串}:替换所有旧字符串为新字符串。- 新字符串为空则删除旧字符串。
- 字符串替换不会改变变量本身的值,只是得到一串字符。
// 字符串替换
]# a=12341234
]# echo ${a/3/78}
127841234
]# echo ${a//3/78}
1278412784
]# echo ${a/3/}
1241234
]# echo ${a//3/}
124124
]# b= // 定义变量b为空
]# echo ${a//3/$b}
124124
]# echo $a // 字符串替换不会改变变量本身的值。
12341234
字符串的删除 ${#}、${%}
${变量名#字符串}:掐头,从第一个字符开始匹配,(最短匹配)。
${变量名%字符串}:去尾,从最后一个字符开始匹配,(最短匹配)。${变量名##字符串}:掐头,从第一个字符开始匹配,(最长匹配)。
${变量名%%字符串}:去尾,从最后一个字符开始匹配,(最长匹配)。
// 字符串的删除,最短匹配
]# b=abcdefg
]# echo ${b#abc} // 掐头abc
defg
]# echo ${b%efg} // 去尾efg
abcd
]# echo ${b%e} // 无效,去尾一定要从最后一个字符开始匹配
abcdefg
]# echo ${b%e*}
abcd
]# echo ${b#c} // 无效,掐头一定要从第一个字符开始匹配
abcdefg
]# echo ${b#*c}
defg
// 最短匹配和最长匹配的区别
]# c=1234512345
]# echo ${c#*3} // 匹配最短的"*3"
4512345
]# echo ${c##*3} // 匹配最长的"*3"
45
]# echo ${c%4*} // 匹配最短的"4*"
12345123
]# echo ${c%%4*} // 匹配最长的"4*"
123
]# echo ${c#*2*} // 掐头最末的"*"无效
34512345
]# echo ${c%*2*} // 掐头最初的"*"无效
123451
// 应用:给文件改后缀名
]# touch /mnt/abc{01..10}.txt
]# cd /mnt
mnt]# ls
abc10.txt abc2.txt abc4.txt abc6.txt abc8.txt
abc1.txt abc3.txt abc5.txt abc7.txt abc9.txt
mnt]# vim /opt/change.sh
#!/bin/bash
for i in $(ls *.txt)
do
mv $i ${i%txt}doc // 删除字符串txt后拼接字符串doc
done
mnt]# bash /opt/change.sh
mnt]# ls
abc10.doc abc2.doc abc4.doc abc6.doc abc8.doc
abc1.doc abc3.doc abc5.doc abc7.doc abc9.doc
变量初始值的处理 ${:-}
${变量名:-字符串}:当该变量存在且非空时返回变量本身的值,否则返回该字符串。不输出内容。
// 变量初始值的处理
]# a=123456
]# echo ${a:-test}
123456
]# unset a
]# echo ${a:-test}
test
// 应用:创建用户默认密码123456
]# vim /opt/moren.sh
#!/bin/bash
read -p "请输入用户名:" user
[ -z $user ] && exit
read -p "请输入密码(初始密码123456):" pass
useradd $user
echo "${pass:-123456}" | passwd --stdin $user
正则表达式 BRE、ERE
- 正则表达式(Regular Expression):使用一串符号来描述有共同属性的数据。
基本正则 BRE
-
基本正则(BRE,Basic Regular Expressions)的使用:
grep、egrep、…… -
基本正则表达式(写在双引号内):
^:匹配行首。例如:^a以a开头。$:匹配行尾。例如:a$以a结尾。^$:匹配空行(空格行非空行)。[]:集合,匹配集合中的任意单个字符。例如:[abc]匹配 a 或 b 或 c 。
[a-z]:匹配a到z的任意单个字符。
[0-9a-zA-Z]:匹配所有数字字母的任意单个字符。[^]:匹配集合外的任意单个字符。例如:[^abc]匹配 a、b、c之外的任意单个字符 。
[^0-9a-zA-Z]:匹配所有数字字母之外的任意单个字符。.:匹配任意单个字符,包括空格。例如:r..t匹配r与t之间有任意两个字符的字符串。*:匹配前一个字符任意次数(不允许单独使用)。例如:ro*t匹配r与t之间有任意个o的字符串,可匹配rt。.*:匹配任意内容,包括空格行和空行。\{n,m\}:匹配前一个字符n到m次。例如:ro\{1,3\}t匹配r与t之间有1到3个o的字符串,即只匹配rot、root、rooot。\{n,\}:匹配前一个字符n次及以上。例如:ro\{1,\}t匹配r与t之间有1个及以上o的字符串,可匹配rot。\{n\}:匹配前一个字符n次。例如:ro\{1\}t匹配r与t之间有1个o的字符串,即只匹配rot。\(字符串\):将字符串组合为整体;捕获并保留。例如:\(ro\)\{2\}只匹配roro。\1调用第一个保留项,\2调用第二个保留项,……
// 基本正则,grep 或 egrep
]# cat /opt/BRE.txt
// 空行,不含空格
// 空格行,在终端中选择空行和空格行可看出区别
a
b
c
rt
rot
root
rooot
rotro
roro
]# grep ^a /opt/BRE.txt // 以a开头的行
a
]# grep o$ /opt/BRE.txt // 以o结尾的行
rotro
roro
]# grep ^$ /opt/BRE.txt // 空行
// 空行,不含空格
]# grep [abc] /opt/BRE.txt // 含a或b或c的行
a
b
c
]# grep [a-c] /opt/BRE.txt // 含a或b或c的行
a
b
c
]# grep [^a-crt] /opt/BRE.txt // 含abcrt之外字符的行,包括空格
// 含有空格字符
rot
root
rooot
rotro
roro
]# grep "r..o" /opt/BRE.txt // r与o之间有任意两个字符的行
rooot
roro
]# grep "ro*r" /opt/BRE.txt // r与r之间有任意个o的行
roro
]# grep "r.*r" /opt/BRE.txt // r与r之间有任意内容的行
rotro
roro
]# grep "ro\{1,2\}t" /opt/BRE.txt // r与t之间有1到2个o的行
rot
root
rotro
]# grep "ro\{1,\}t" /opt/BRE.txt // r与t之间有至少1个o的行
rot
root
rooot
rotro
]# grep "ro\{1\}t" /opt/BRE.txt // 含rot的行
rot
rotro
]# grep "\(ro\)\{2\}" /opt/BRE.txt // 含roro的行
roro
]# grep "." /opt/BRE.txt // 含任意单个字符(包括空格)的行
// 空格行
a
b
c
rt
rot
root
rooot
rotro
roro
]# grep ".*" /opt/BRE.txt // 含任意内容的行,包括空行和空格行
// 空行
// 空格行
a
b
c
rt
rot
root
rooot
rotro
roro
扩展正则 ERE
-
扩展正则(ERE,Extended Regular Expressions)的使用:
grep -E(ERE)、egrep、…… -
扩展正则表达式(写在双引号内):
(字符串):将字符串组合为整体;捕获并保留。\1调用第一个保留项,\2调用第二个保留项,……{n,m}:匹配前一个字符n到m次。{n,}:匹配前一个字符n次及以上。{n}:匹配前一个字符n次。+:匹配前一个字符最少一次。相当于{1,}。?:匹配前一个字符最多一次。相当于{0,1}。|:或者。例如:root|sh$表示字符串root或以sh结尾。\b(boundary):单词边界,不能有数字字母下划线。例如:the\b或the\>表示e是单词右边界,\bthe或\<the表示t是单词左边界,\bthe\b或\<the\>表示单词the。- 区别:
$是行结尾,\>是单词右边界。
- 区别:
\w(word):匹配数字、字母、下划线。\s(space):匹配空格和制表符。\d(digit):匹配数字。相当于[0-9],但\d兼容性不好,grep -P "\d" 内容。\t(tabulation character):表示一个制表符。
// 扩展正则,grep -E 或 egrep
]# cat /opt/EXT.txt
rotrot
rt
rot
root // 两个o
rooot // 三个o
roooot // 四个o
bash
]# egrep "ro{2,3}t" /opt/EXT.txt // r与t之间有2到3个o的行
root
rooot
]# egrep "ro{2,}t" /opt/EXT.txt // r与t之间至少2个o的行
root
rooot
roooot
]# egrep "ro{2}t" /opt/EXT.txt // 含root的行
root
]# egrep "(rot){2}" /opt/EXT.txt // 包含rotrot的行
rotrot
]# egrep "ro+t" /opt/EXT.txt // r与t之间至少1个o的行,可写为ro{1,}t
rotrot
rot
root
rooot
roooot
]# egrep "ro?t" /opt/EXT.txt // r与t之间至多1个o的行,可写为ro{0,1}t
rotrot
rt
rot
]# egrep "root|sh$" /opt/EXT.txt // 含root或以sh结尾的行
root
bash
]# cat /opt/THE.txt
atheb
the_
_the
:the
the:
the
]# egrep "the" /opt/ext.txt
atheb
the_
_the
:the
the:
the
]# egrep "\bthe" /opt/ext.txt // t左边不含数字字母下划线
// 也可以写作 egrep "\<the" /opt/ext.txt
the_
:the
the:
the
]# egrep "the\b" /opt/ext.txt // e右边不含数字字母下划线
// 也可以写作 egrep "the\>" /opt/ext.txt
_the
:the
the:
the
]# egrep "\bthe\b" /opt/ext.txt
// 也可以写作 egrep "\<the\>" /opt/ext.txt
:the
the:
the
匹配IP地址的正则
// 匹配IP地址的正则表达式
// 250-255
25[0-5]
// 200-249
2[0-4][0-9]
// 0-199
1?[0-9]?[0-9]
// 0-255
25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9]
// 前24位
(25[0-5]\.|2[0-4][0-9]\.|1?[0-9]?[0-9]\.){3}
// 32位
(25[0-5]\.|2[0-4][0-9]\.|1?[0-9]?[0-9]\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])
sed
- 流式编辑器(Stream Editor,sed命令):非交互式对文档进行增删改查等操作。逐行处理,并将处理结果输出到屏幕。
sed命令
有输出内容的前置命令 | sed [选项] '条件指令'或sed [选项] '条件指令' 文件路径-
选项:
-n:屏蔽默认输出。sed命令默认输出读取的内容。-i:修改源文件,没有输出。sed命令默认不修改源文件。与选项-E连用时只写作-Ei。-r或-E(ERE):支持扩展正则。sed命令默认只支持基本正则。-e(expression):指定要执行的编辑命令,一条sed命令指定多个编辑命令可以使用-e。sed [选项] -e '条件指令1' -e '条件指令n' 文件路径
-
条件(也叫定址符,写在单引号内,指令前面):指定符合条件的行,省略默认所有行
/正则表达式/:查找内容。$:最后一行。行号:直接写在指令前面。
行号!:除该行之外的所有行。m:第m行。
m!:第m行之外的所有行。m,n:第m到n行。
m,n!:第m到n行之外的所有行。m,+n:第m行以及其后的n行。共n+1行。
m,+n!:第m行以及其后的n行之外的所有行。m~n:第m行以及每次步进n行。
m~n!:第m行以及每次步进n行之外的所有行。mp;np:输出第m行和第n行。
md;nd:删除第m行和第n行,也就是输出第m行和第n行之外的所有行。
-
指令(写在单引号内,同指令多指令用
;隔开):-
=:输出行号。'条件=',选项-n、-E。
-
p(print):输出整行(可理解为查找)。'条件p',选项-n、-E。
-
d(delete):删除整行,会输出其余所有行,可看作p的相反指令。'条件d',选项-E、-i。
-
s(substitute):替换部分,替换为空是删除旧字符串。 -
'条件s/旧字符串/新字符串/flags标记',选项-n、-E、-i。- flags标记项:
数字(行内第几个匹配项)、g(行内所有匹配项)、p(输出匹配行)
- flags标记项:
-
a(append):行下追加整行内容。'条件a 内容',选项-E、-i。
-
i(insert):行上添加整行内容。'条件i 内容',选项-E、-i。
-
c(replace):替换整行内容。'条件c 内容',选项-E、-i。
-
-
p输出
p(print):输出整行(可理解为查找)。'条件p',选项-n、-E。
// sed p输出。'条件p'
]# head -5 /etc/passwd | cat -n > /opt/user.txt
]# cat /opt/user.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed 'p' /opt/user.txt // 指令'p'默认输出所有行
1 root:x:0:0:root:/root:/bin/bash // 指令处理结果
1 root:x:0:0:root:/root:/bin/bash // 默认输出,输出读取的内容
2 bin:x:1:1:bin:/bin:/sbin/nologin // 逐行处理
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '1p' /opt/user.txt // 选项-n屏蔽sed默认输出,条件指令'1p'是输出第1行
1 root:x:0:0:root:/root:/bin/bash
]# sed -n '1!p' /opt/user.txt // 输出第1行之外的所有行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '2p;4p' /opt/user.txt // 输出第2行和第4行
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# sed -n '2,4p' /opt/user.txt // 输出第2到4行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# sed -n '2,4!p' /opt/user.txt // 输出第2到4行之外的所有行
1 root:x:0:0:root:/root:/bin/bash
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '2,+3p' /opt/user.txt // 输出第2行以及其后3行,即2345行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '2,+3!p' /opt/user.txt // 输出第2行以及其后3行之外的所有行,即1行
1 root:x:0:0:root:/root:/bin/bash
]# sed -n '1~2p' /opt/user.txt // 输出第1行以及每次步进2行,即第135行
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '1~2!p' /opt/user.txt // 输出第1行以及每次步进2行之外的所有行,即24行
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# sed -n '/root/p' /opt/user.txt // 条件指令'/root/p'是输出含有root字符串的行(输出结果没有颜色标记)
1 root:x:0:0:root:/root:/bin/bash
??
]# sed -n '!/root/p' /opt/user.txt
]# sed -nE '/bash$|dae/p' /opt/user.txt // 选项-E是支持扩展正则
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
]# sed -n '$p' /opt/user.txt // 条件指令'$p'是输出最后一行
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
=输出行号
=:输出行号。'条件=',选项-n、-E。
// sed =输出行号。'条件='
]# head -5 /etc/passwd | cat -n > /opt/user.txt
]# cat /opt/user.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '=' /opt/user.txt // 指令'='默认输出所有行的行号
1
2
3
4
5
]# sed -n '2,+3=' /opt/user.txt // 输出第二行以及其后三行的行号
2
3
4
5
]# sed -n '1~2=' /opt/user.txt // 输出第一行以及每次步进两行的行号
1
3
5
]# sed -n '$=' /opt/user.txt // 条件指令'$='是输出最后一行的行号
5
]# sed -n '/bash$/=' /opt/user.txt // 条件指令'/bash$/='是输出以bash字符串结尾的行的行号
1
]# sed -nE '/bash$|dae/=' /opt/user.txt // 选项-E支持扩展正则
1
3
d删除
d(delete):删除整行。'条件d',选项-E、-i。
// sed d删除。'条件d'
]# head -5 /etc/passwd | cat -n > /opt/user.txt
]# cat /opt/user.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '1d' /opt/user.txt // 条件指令'1d'是删除第1行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '1!d' /opt/user.txt // 删除第1行之外的所有行
1 root:x:0:0:root:/root:/bin/bash
]# sed '2d;4d' /opt/user.txt // 删除第2行和第4行
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -n '2p;4p' /opt/user.txt // 输出第2行和第4行
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# sed '2,4d' /opt/user.txt // 删除第2到4行
1 root:x:0:0:root:/root:/bin/bash
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '2,4!d' /opt/user.txt // 删除第2到4之外的所有行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# sed '2,+3d' /opt/user.txt // 删除第2行以及其后3行,即2345行
1 root:x:0:0:root:/root:/bin/bash
]# sed '2,+3!d' /opt/user.txt // 删除第2行以及其后3行之外的所有行,即1行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '1~2d' /opt/user.txt // 删除第1行以及每次步进2行,即第135行
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# sed '1~2!d' /opt/user.txt // 删除第1行以及每次步进2行之外的所有行,即第246行
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '/root/d' /opt/user.txt // 删除含有root字符串的行
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -E '/bash$|dae/d' /opt/user.txt // 选项-E是支持扩展正则
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '$d' /opt/user.txt // 删除最后一行
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
]# cat /opt/user.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed -Ei '/bash$|dae/d' /opt/user.txt // 选项-i是修改源文件,与选项-E连用只能写作-Ei
]# cat /opt/user.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
s替换部分
s(substitute):替换部分,替换为空是删除旧字符串。'条件s/旧字符串/新字符串/[flags标记]',选项-n、-E、-i。- flags标记项:
数字(行内第几个匹配项,省略默认第一个)、g(行内所有匹配项)、p(输出匹配行)。 - 注意:旧字符串和新字符串均是正则表达式。
- 新字符串使用
&代表旧字符串匹配到的全部内容。 - 旧字符串使用
(正则表达式)捕获并保留时,新字符串使用\数字来调用第数字个()的保留内容。 - 注意:这里的
///也可以用其它符号或空格代替,只需要前后一致就行,例如:条件/s?旧字符串?新字符串?[flags标记]
- flags标记项:
// sed s替换。'条件s/旧字符串/新字符串/flags标记'
]# cat /opt/num.txt
2017 2011 2018
2017 2017 2024
2017 2017 2017
]# sed 's/2017/1009/' /opt/num.txt // 指令s默认替换所有行的第一个匹配项。替换所有行的第一个2017为1009
1009 2011 2018
1009 2017 2024
1009 2017 2017
]# sed 's/2017/1009/2' /opt/num.txt // 最后数字2是指令s的标记项,代表行内第二个匹配项。替换所有行的第二个2017
2017 2011 2018
2017 1009 2024
2017 1009 2017
]# sed '2s/2017/1009/' /opt/num.txt // 替换第二行的第一个2017
2017 2011 2018
1009 2017 2024
2017 2017 2017
]# sed '2s/2017/1009/2' /opt/num.txt // 替换第二行的第二个2017
2017 2011 2018
2017 1009 2024
2017 2017 2017
]# sed 's/2017/1009/g' /opt/num.txt // g是指令s的标记项,代表行内所有匹配项。替换所有行的所有2017
1009 2011 2018
1009 1009 2024
1009 1009 1009
]# sed '/2024/s/2017/1009/g' /opt/num.txt // 替换含2024字符串的行的所有2017
2017 2011 2018
1009 1009 2024
2017 2017 2017
]# sed -n '/2024/s/2017/1009/p' /opt/num.txt // 屏蔽默认输出,p是指令s的标记项,代表输出匹配行。
1009 2017 2024
]# sed -n '/2024/s/2017/1009/gp' /opt/num.txt // 标记项pg连用。替换含2024字符串的行的所有2017并输出
1009 1009 2024
]# cat /opt/num.txt
2017 2011 2018
2017 2017 2024
2017 2017 2017
]# sed -i 's/2017/1009/2' /opt/num.txt // 选项-i表示修改源文件
]# cat /opt/num.txt
2017 2011 2018
2017 1009 2024
2017 1009 2017
// 将第一行"/bin/bash"替换为"/sbin/sh"
]# sed '1s/\/bin\/bash/\/sbin\/sh/' /opt/user.txt
// 只要使用三个同样的符号就行:sed '1s#/bin/bash#/sbin/sh#' /opt/user.txt
// 也可以用空格:sed '1s /bin/bash /sbin/sh ' /opt/user.txt
1 root:x:0:0:root:/root:/sbin/sh
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
s替换与()保留
(内容1)(内容2):捕获并保留内容1、内容2。内容支持正则表达式
\数字:调用第数字个()的保留内容。
// s替换与()保留
// 原内容"成绩 人名",现需要"人名 成绩"
]# cat /opt/score.txt
100 laowang
98 gangge
59 laoniu
]# sed -Ei 's/([0-9]+)(\s+)([a-z]+)/\3\2\1/' > /opt/score.txt
]# cat /opt/score.txt
laowang 100
gangge 98
laoniu 59
a、i、c 整行内容
a(append):行下追加整行内容。'条件a 内容',选项-E、-i。
i(insert):行上添加整行内容。'条件i 内容',选项-E、-i。
c(replace):替换整行内容。'条件c 内容',选项-E、-i。
// sed a行下追加,i行上添加、c替换整行
]# head -5 /etc/passwd | cat -n > /opt/user.txt
]# cat /opt/user.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed 'a 1009' /opt/user.txt // 指令a默认所有行下追加整行内容
1 root:x:0:0:root:/root:/bin/bash
1009
2 bin:x:1:1:bin:/bin:/sbin/nologin
1009
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
1009
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
1009
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
1009
]# sed '2a 1009' /opt/user.txt // 条件指令'2a 1009'表示第二行下追加整行内容1009
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
1009
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '/sh$/a 1009' /opt/user.txt // 条件指令'/sh$/a 1009'表示以sh结尾的行下追加整行内容1009
1 root:x:0:0:root:/root:/bin/bash
1009
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '/sh$/i 1009' /opt/user.txt // 条件指令'/sh$/i 1009'表示以sh结尾的行上添加整行内容1009
1009
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '$i 1009' /opt/user.txt // 条件指令'$i 1009'表示末行上添加整行内容1009
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
1009
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# sed '$c 1009' /opt/user.txt // 条件指令'$c 1009'表示末行上替换整行内容1009
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
1009
脚本搭建httpd服务(82端口)
// 脚本搭建httpd服务并监听82端口
/* 如果搭建过httpd网站,停止服务、卸载软件并删除所有配置
* 搭建httpd网站服务:装包、配置、起服务(selinux宽松0)
*/
虚拟机(192.168.166.2):
]# vim /opt/httpd82.sh
#!/bin/bash
systemctl stop httpd.service &> /dev/null // 停止服务
yum -y remove httpd &> /dev/null // 卸载软件
rm -rf /etc/httpd // 删除配置文件父目录
yum install -y httpd > /dev/null // 装包
sed -i '/^Listen 80$/s/80/82/' /etc/httpd/conf/httpd.conf // 配置,修改端口号
setenforce 0 // 更改selinux为宽松0
systemctl restart httpd > /dev/null // 启服务
systemctl enable httpd > /dev/null // 开机自启服务
echo "sed-test~~" > /var/www/html/index.html // 编写默认网页文件
]# bash /opt/httpd82.sh
]# curl 192.168.166.2
sed-test~~
]# ss -ntulp | grep httpd // 查看httpd服务端口号
]# systemctl stop firewalld // 如果其它机器访问本机httpd服务,本机需要关闭防火墙
脚本提取用户名和密码串
// 提取用户名和密码串
// 找出使用bash的所有用户名,并找到对应的加密密码串,最后输出"用户名 --> 加密密码串"
]# vim /opt/userpass.sh
#!/bin/bash
/* 找出使用bash的所有用户名
* 写法一:
* grep "/bash" /etc/passwd | sed 's/:.*//' // :.*代表冒号:加上任意内容
* 写法二:
* sed -n '/bash$/s/:.*//p' /etc/passwd // 选项-n屏蔽默认输出,标记项p输出匹配行
*/
user=$(sed -n '/bash$/s/:.*//p' /etc/passwd)
for name in $user
do
pass=$(grep $name /etc/shadow)
pass=${pass#*:}
pass=${pass%%:*}
echo "$i --> $pass" >> /opt/userpass.txt
done
]# bash /opt/userpass.sh
]# cat /opt/userpass.txt
root --> $6$raxwu9kly0mSJFAz$pIAfYLjLE/wyeCnmxBD/hts/pCFEOgUw582IlB3WOoEO9.IxfEgGPHBjwqfY813nONvn9z0kM7N7eWliVzHkH0
test166-2 --> $6$xy2FCve.Z.jv5m4s$UcX0Yz1gntkzvD.roeQv2f0NRHhvG/vkcODWB.VpIm9.1HeBNnEEZSw.sZlyvnHy5GCLf2SamcmPaEliBEsRb/
zhangsan --> $6$uTVKaSFA90adc35J$XK15WTIlDGu5HZq1ySlHb.VwslvXok2VSXnPr2D6PDzHohGJkYgl653KRlHfRou3ZBOw1/v7EWjkHm1nxiuCV.
wangwu --> $6$4hM6Xp7ReRctbL/4$.baJp758Gvi64B9yyt8IoLNwzVIQFRYRRXZGsUoMrpiO0OBvmZNuHwghKP/Y/FYRWEZQc.r4m95cal0did1hU0
awk
- awk是创造者Aho Weinberger Kernaighan三个人的首字母缩写。
- 相比grep和sed,awk是精确搜索。
- awk同sed一样也是逐行处理,并将处理结果输出到屏幕。
- Shell编程三剑客:grep、sed、awk
awk命令
有输出内容的前置命令 | awk [选项] '条件{指令 内置变量}'或awk [选项] '条件{指令 内置变量}' 文件路径- 选项:
-F:定义分隔符。支持扩展正则。- awk命令默认分隔符为空格或制表符,按照分隔符分列(字段)。
-F分隔符、-F[分隔符1分隔符2](分隔符1或分隔符2)
- 条件用法同sed命令的条件(定址符):指定符合条件的行,省略默认所有行。awk命令默认支持扩展正则。
- 指令(写在{}内,多指令用
;隔开):print:输出。- 当有条件且纯print指令即
条件{print}时,可以简写为条件。
- 内置变量(写在{}内):
$数字(第几列/字段)、$0(所有列/字段,整行)、NR(Number of Records,行号)、NF(Number of Fields,列数/字段数)、$NF(最后一列/字段),:表示一个空格字符。"字符串":该字符串是常量。\t(tabulation character):表示一个制表符,写在""内。
- 注意:
awk [选项] '条件{指令 内置变量}' 文件路径:这里使用单引号'内容'时,内容可以支持内置变量,但不支持调用awk语句外的变量var,解决方法使用三个单引号''' $var'''。
- 选项:
// awk命令
]# cat /opt/awktest.txt
hello world
welcome to Beijing
]# awk '{print}' /opt/awktest.txt // 指令print表示输出,无条件时默认所有行
// 无条件时不可省略{print}
hello world
welcome to Beijing
]# awk '/to/{print}' /opt/awktest.txt // 含to的行
// 有条件且纯{print}时,可以简写为:awk '/to/' /opt/awktest.txt
welcome to Beijing
]# awk '/to/{print $0}' /opt/awktest.txt // 含to的行的所有列
welcome to Beijing
]# awk '/to/{print $1,$3}' /opt/awktest.txt // 含to的行的第1列和第3列
welcome Beijing
]# awk '/to/{print NR}' /opt/awktest.txt // 含to的行的行号
2
]# awk '/to/{print NF}' /opt/awktest.txt // 含to的行的列数
3
// 选项-F定义分隔符,默认分隔符是空格
]# awk '/^root/{print}' /etc/passwd // 以root开头的行
root:x:0:0:root:/root:/bin/bash
]# awk '/^root/{print NF}' /etc/passwd // 该行默认分隔符为空格或制表符的列数
1
]# awk -F: '/^root/{print NF}' /etc/passwd // 该行分隔符为":"的列数
7
]# awk -F/ '/^root/{print NF}' /etc/passwd // 该行分隔符为"/"的列数
4
]# awk -F[:/] '/^root/{print NF}' /etc/passwd // 该行分隔符为":"或"/"的列数
10
]# awk '/^root/{print $1}' /etc/passwd // 该行分隔符为空格或制表符的第一列
root:x:0:0:root:/root:/bin/bash
]# awk -F: '/^root/{print $1}' /etc/passwd // 该行分隔符为":"的第一列
root
# awk -F: '/^root/{print $1,$6}' /etc/passwd // ","表示空格字符
root /root
]# awk -F: '/^root/{print $1$6}' /etc/passwd // 不使用","的效果
root/root
]# awk -F: '/^root/{print $1"的家目录是:"$6}' /etc/passwd // "字符串"
root的家目录是:/root
// awk命令单引号的使用'条件{指令 内置变量}'
]# a="root"
]# echo $a
root
]# awk -F: '/^$a/{print $1"的家目录是:"$6}' /etc/passwd // $a识别为字符串$a
]# awk -F: '/^'''$a'''/{print $1"的家目录是:"$6}' /etc/passwd // '''$a'''识别为变量a,字符串root
root的家目录是:/root
监控网卡的流量信息
// 监控网卡eht0的流量信息
]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.166.2 netmask 255.255.255.0 broadcast 192.168.166.255
inet6 fe80::3dbe:e761:bedf:b1a8 prefixlen 64 scopeid 0x20<link>
ether 52:54:00:81:4a:5a txqueuelen 1000 (Ethernet)
RX packets 11263 bytes 834102 (814.5 KiB) // 实时接收的数据流量
RX errors 0 dropped 34 overruns 0 frame 0
TX packets 2893 bytes 643396 (628.3 KiB) // 实时发送的数据流量
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
]# ifconfig eth0 | awk '/RX p/{print "eth0网卡接收数据流量"$5"字节"}'
eth0网卡接收数据流量891169字节
]# ifconfig eth0 | awk '/TX p/{print "eth0网卡接收数据流量"$5"字节"}'
eth0网卡接收数据流量682077字节
]# ifconfig eth0 | awk '/TX p/{print "eth0网卡接收数据流量"$5"字节"}'
eth0网卡接收数据流量682645字节
awk处理的时机
- awk会逐行处理文本,支持在处理第一行之前做一些准备工作,以及在处理完最后一行之后做一些总结性质的工作。
BEGIN{}:读取文件前执行,在所有行之前处理,指令执行1次,叫做行前处理任务。{}:读取文件时执行,逐行处理,指令执行n次,叫做逐行处理任务。END{}:读取文件后执行,在所有行之后处理,指令执行1次,叫做行后处理任务。- 三者可单独使用,也可以一起使用。在
{}中定义的变量可以传递使用。
- 三者可单独使用,也可以一起使用。在
// awk处理的时机??
]# cat /opt/awktest.txt // 准备四行内容
a
b
c
d
]# awk 'BEGIN{print "ok"}{print "abc"}END{print "end"}' /opt/awktest.txt // 一起使用
ok // 读取文件前输出ok
abc // 逐行读取第一行输出abc
abc // 逐行读取第二行输出abc
abc // 逐行读取第三行输出abc
abc // 逐行读取第四行输出abc
end // 读取文件后输出end
]# awk 'BEGIN{print NR}{print NR}END{print NR}' /opt/awktest.txt
0 // 读取文件前,当前行号为0
1 // 逐行读取第一行时的行号
2 // 逐行读取第二行时的行号
3 // 逐行读取第三行时的行号
4 // 逐行读取第四行时的行号
4 // 读取文件后,当前行号为最后一次读取的行的行号
统计用户信息
// 制作表格:用户名 UID 家目录,最后统计用户数。
]# head -5 /etc/passwd > /opt/awkuser.txt
]# cat /opt/awkuser.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
]# awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1"\t"$3"\t"$6}END{print "All",NR,"records."}' /opt/awkuser.txt
User UID Home
root 0 /root
bin 1 /bin
daemon 2 /sbin
adm 3 /var/adm
lp 4 /var/spool/lpd
All 5 records.
// 统计使用bash解释器的用户数
]# awk 'BEGIN{x=0}/\/bash$/{x++}END{print "总共有"x"个用户使用bash解释器。"}' /etc/passwd
总共有3个用户使用bash解释器。
awk处理的条件
-
$数字~/正则/:该数字列满足该正则的行。
$数字!~/正则/:该数字列不满足该正则的行。 -
数值1比较符数值2:符合该数值比较的行。- 比较符:
==、!=、>、>=、<、<=。
- 比较符:
-
条件1&&条件2:同时满足条件1和条件2的行。注意:同条件测试逻辑与不一样。
条件1||条件2:满足条件1或条件2的行。注意:同条件测试逻辑或不一样。 -
运算比较符数值:运算结果符合数值比较的行。
// awk处理的条件
]# head -5 /etc/passwd > /opt/awkuser.txt
]# cat /opt/awkuser.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
// 第一类,~与!~
]# awk -F: '$6~/root/{print}' /opt/awkuser.txt // 输出第6列满足包含root的行
root:x:0:0:root:/root:/bin/bash
]# awk -F: '$6!~/root/{print}' /opt/awkuser.txt // 输出第6列不满足包含root的行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
// 第二类,数值比较
]# awk -F: '$3==2{print}' /opt/awkuser.txt // 输出第3列数值等于2的行
daemon:x:2:2:daemon:/sbin:/sbin/nologin
]# awk -F: '$3<=2{print}' /opt/awkuser.txt // 输出第3列数值小于等于2的行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
]# awk -F: 'NR<=2{print}' /opt/awkuser.txt // 输出行号小于等于2的行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
// 第三类,&&与||
]# awk -F: 'NR<=2&&$7~/bash/{print}' /opt/awkuser.txt // 输出行号小于等于2并且第7列包含bash的行
root:x:0:0:root:/root:/bin/bash
]# awk -F: 'NR<=2||$7~/bash/{print}' /opt/awkuser.txt // 输出行号小于等于2或第7列包含bash的行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
]# awk -F: 'NR>4&&$7~/bash/{print}' /opt/awkuser.txt // 输出行号大于4并且第7列包含bash的行
]# awk -F: 'NR>4||$7~/bash/{print}' /opt/awkuser.txt // 输出行号大于4或第7列包含bash的行
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
// 第四类,数学运算
]# awk -F: 'NR%2==0{print NR,$0}' /opt/awkuser.txt // 输出偶数行的行号和所有列
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
awk 'NR%2==0{print NR,$0}' user #在条件中使用运算,找到将行号除以2余数等于0的行,然后输出该行的行号和所有列,相当于输出偶数行
awk数组、for、if
-
数组是一个可以存储多个值的变量。
- 定义数组:
数组名[索引]=元素值,变量不赋值默认初始值为0。- 索引只能使用数字或"字符串",元素赋值也只能使用数字或"字符串"。
- 调用数组:
数组名[索引]
- 定义数组:
-
awk命令中大括号内的 for语句:
for(变量名 in 数组名){print 变量名,数组名[变量名]}- 注意:awk数组使用for语句,只能循环数组的索引。
-
awk命令中大括号内的 if语句:
if(数组名[变量名]>=3){print 变量名,数组名[变量名]}- 注意:if语句的变量名是数组的索引。
// awk数组
// 索引只能使用数字或"字符串"
]# awk 'BEGIN{a=10;a=20;print a}'
20
]# awk 'BEGIN{a["abc"]=10;a[1]="xyz";print a["abc"],a[1]}'
10 xyz
]# awk 'BEGIN{a[1]=10;a[a]=20;print a[1],a[a]}' // 索引a识别为数组a
awk: 命令行:1: 致命错误:试图在标量环境中使用数组“a”
// 元素赋值只能使用数字或"字符串"
]# awk 'BEGIN{a["*"]=10;a[1]=bs;print a["*"],a[1]}' // 元素赋值bs识别无效
10
]# awk 'BEGIN{a["*"]=10;a[1]=a;print a["*"],a[1]}' // 元素赋值a识别为数组a
awk: 命令行:1: 致命错误:试图在标量环境中使用数组“a”
]# awk 'BEGIN{a["*"]=10;a[1]="a";print a["*"],a[1]}' // 元素赋值"a"识别为字符串
10 a
// 遍历数组,索引只能使用数字或"字符串",元素赋值也只能使用数字或"字符串"
]# awk 'BEGIN{a[1]=1;a[b]=b;a[_]=_;a["test"]=test;for(i in a){print i,a[i]}}' // 索引字母下划线失效
test
1 1
]# awk 'BEGIN{a[1]=1;a["b"]=b;a["_"]=_;a["test"]=test;for(i in a){print i,a[i]}}' // 索引使用数字或"字符串"
_
b
test
1 1
]# awk 'BEGIN{a[1]=1;a["b"]="b";a["_"]="_";a["test"]="test";for(i in a){print i,a[i]}}' // 元素赋值使用数字或"字符串"
_ _
b b
test test
1 1
// 变量++,统计同行出现次数
]# cat /opt/shuzu.txt
abc
xyz
abc
opq
xyz
abc
/* 逐行处理任务{a[$1]++},变量不赋值默认初始值为0
* 第一行:a["abc"]++,得到a["abc"]=1
* 第二行:a["xyz"]++,得到a["xyz"]=1
* 第三行:a["abc"]++,得到a["abc"]=2
* 第四行:a["opq"]++,得到a["opq"]=1
* 第五行:a["xyz"]++,得到a["xyz"]=2
* 第六行:a["abc"]++,得到a["abc"]=3
*/
]# awk '{a[$1]++}END{print a["abc"]}' /opt/shuzu.txt
3
]# awk '{a[$1]++}END{print a["xyz"]}' /opt/shuzu.txt
2
]# awk '{a[$1]++}END{print a["opq"]}' /opt/shuzu.txt
1
]# awk '{a[$1]++}END{for(i in a){print i,a[i]}}' /opt/shuzu.txt
opq 1 // opq出现了1次
abc 3 // abc出现了3次
xyz 2 // xyz出现了2次
// 查找重复的行,即1次以上
]# awk '{a[$1]++}END{for(i in a){if(a[i]>1){print i,a[i]}}}' /opt/shuzu.txt
abc 3
xyz 2
二维数组
Arr[2,79]=78
Arr[2,79]表示一个二维数组,第一个索引为2,第二个索引为79。这里是给Arr[2,79]赋值为78。这就意味着在数组Arr中的第2行、第79列的位置上的元素值为78
SUBSEP是awk中的一个特殊变量,用于分隔多维数组的不同维度。默认情况下,SUBSEP的值是`\034`。
因此Arr[2,79]可以写为Arr[2 SUBSEP 79]或Arr[2 \034 79]
统计web访问量??
-
/var/log/httpd/access_log:httpd服务的访问日志。 -
sort命令选项:
-n(numeric):按照数字排序。-r(reverse):降序。-k(key):指定按照第几字段排序(字段从1起计,多字段使用,分隔)。t:指定字段分隔符。
// 统计web访问量
]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
192.168.166.1 3
192.168.166.2 5
192.168.166.3 2
192.168.166.240 2
]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log | sort -nr -k 2
192.168.166.2 5
192.168.166.1 3
192.168.166.3 2
192.168.166.240 2
统计登陆系统失败的IP
/var/log/secure:系统安全日志。
// 统计以root登陆系统失败的IP
]# awk '/Failed password for root/{ip[$11]++}END{for(i in ip){print i,ip[i]}}' /var/log/secure
192.168.166.2 2
192.168.166.3 3
// 统计以root登陆系统失败3次以上的IP
]# awk '/Failed password for root/{ip[$11]++}END{for(i in ip){if(ip[i]>=3){print i,ip[i]}}}' /var/log/secure
监控系统信息
uptime:截取top动态的第一行。free:查看内存(Mem物理内存和Swap虚拟内存)。df -h:查看挂载点详细信息。who:查看当前登录本机的用户数。- ??
// 监控系统信息
]# cat /opt/test.sh
#!/bin/bash
while:
do
clear
uptime | awk '{print "CPU的15分钟平均负载量是:"$NF}'
free -h | awk '/^Mem/{print "剩余内存容量是:"$4}'
df -h | awk '/\/$/{print "根分区剩余容量是:"$4}'
who | awk 'END{print "目前使用服务器人数是:"NR}'
awk 'END{print "账户总数量是"NR"个"}' /etc/passwd
rpm -qa | awk 'END{print "安装的rpm软件总数是"NR"个"}'
sleep 3
done
脚本文件 /etc/rc.d/rc.local
/etc/rc.d/rc.local(run command)脚本文件,会在Linux系统启动时自动执行。- 如果使用,需要赋予可执行权限
chmod +x /etc/rc.d/rc.local。
- 如果使用,需要赋予可执行权限
括号
命令
`cmd` 或 $(cmd) 均可以用来优先执行命令并获取命令的标准输出,区别在于
1.套用时``要加转义符:cmd1 `cmd2 \`cmd3\``,也可以 cmd1 $(cmd2 $(cmd3))
2.``可以在多种unix shell中执行,$()可以在部分unix shell中执行
(cmd1;cmd2;cmd3) 或 { cmd1;cmd2;cmd3;} 可以用于连接并执行多个命令,区别在于:
1.()新开一个新shell环境执行命令,()内的变量和命令对()外的命令和变量没有影响。
{}在本shell环境中执行命令,{}内的变量和命令对{}外的命令和变量有影响。
2.()内的最后一条命令的;可加可不加。
{}内的最后一条命令的;不可省略。
3.{}的左括号和第一条命令之间必须有空格。
运算与判断
echo {a,b,c}.txt # 不管存不存在,输出a.txt b.txt c.txt
echo {a..c}.txt # 不管存不存在,输出a.txt b.txt c.txt
echo [a-c].txt # 只输出存在的文件
$[exp] 或 $((exp)) # 整数运算,进行不同进制运算时自动转为十进制,不会自动打印结果
echo $((2#10*4)) # 8。2#10表示二进制数10
expr exp # 整数运算,自动打印结果,但遇到特殊符号需要使用转义符
expr 5 \* 2 # 10。特殊符号*需要转义
echo "exp" | bc # 小数运算
echo "scale=3;5/3" | bc # 1.666
((exp)) # 也可以进行算术比较(不能进行字符串比较),括号内的变量可以省略$
((exp)) # 可以对变量进行定义或重新赋值,且之后脚本全部有效。
[ 条件表达式 ] 或 [[ 条件表达式 ]]
1.重定向等字符在[]内是重定向,在[[]]内是比较符号。
2.[[]]会返回一个退出状态码,真则`$?`为0,假则`$?`为1。`perror 数字`查看退出码含义
3.[[]]支持&&、||、>、<
4.[[]]支持算术扩展,[]不支持:可以[[ 1+2 -eq 3 ]]。
5.[[]]支持字符串的模式匹配,[]不支持,但注意不能加引号:[[ "hello" == hell? ]] 为真,[ "hello" == "hell?" ] 为假。
6.[[]]支持正则:[[ exp =~ 正则表达式 ]]
正则
常见元字符:
\d # [0-9]
\D # \d取反,[^0-9]
\w # [A-Za-z0-9_]
\W # \w取反,[^A-Za-z0-9_]
\s # 空白字符
\S # \s取反
\b # 单词边界
\B # \b取反
(exp) :普通捕获组,分配组号,sed或awk中使用`\组号`调用
(?<组名>exp) :命名捕获组,分配组名,sed或awk中使用`\k<组名>`调用
(?:exp) :不捕获组。只是用于限定操作的范围
(?#注释) :注释
.*(?=exp) :(零宽正向先行断言)后面是exp的内容,即匹配exp前面的内容
(?<=exp).* :(零宽正向后行断言)前面是exp的内容,即匹配exp后面的内容
.*(?!exp) :(零宽负向先行断言)后面不是exp的内容
(?<!exp).* :(零宽负向后行断言)前面不是exp的内容
数组
数组名=(元素 元素 元素) # 定义数组,索引从0开始。例如 array=(a b c),则array[0]=a
${数组名[索引]} # 调用数组元素
特殊的,${数组名[0]} 可以简写为${数组名}
${#数组名[索引]} # 该元素的长度
特殊的,${#数组名[0]} 可以简写为${#数组名}
${!数组名[@]}、${!数组名[*]} # 列出所有索引
${数组名[*]}、${数组名[@]} # 遍历所有元素
特殊地,"${数组名[*]}" 代表一个字符串
${数组名[*]}、${数组名[@]} 均可以用于遍历数组的所有元素,但特殊地,"${数组名[*]}" 代表一个字符串,具体区别见下列两个示例
示例一:
]# a=(1 2 3) # 定义一个数组
]# for i in "${a[*]}";do echo $i;done # "${数组名[*]}" 代表一个字符串
1 2 3
]# for i in ${a[*]};do echo $i;done # ${数组名[*]} 代表一个数组
1
2
3
]# for i in "${a[@]}";do echo $i;done # "${数组名[@]}" 代表一个数组
1
2
3
]# for i in ${a[@]};do echo $i;done # ${数组名[@]} 代表一个数组
1
2
3
示例二:
]# cat test.sh
#!/bin/bash
a=(1 2 3)
function test(){
for i in $1
do
echo $i
done
}
test "${a[*]}" # 将"1 2 3"一个字符串作为一个位置参数传递给函数test()
echo "---"
test ${a[*]} # 将1 2 3三个元素作为三个位置参数传递了函数test()
]# sh test.sh
1
2
3
---
1
变量
${var} # 直接引用变量
${!var} # 间接引用变量
${#var} # 变量的长度
${var:-string} # 若变量var为空,则返回string,否则输出var值。不影响原值
${var:=string} # 若变量var为空,则返回string并赋值给var,否则输出var值。影响原值。
${var:+string} # 若变量var不为空,则返回string,否则输出空。不影响原值
${var:?string} # 若变量var不为空,则把string输出到标准错误中并从脚本中退出,否则输出var值。
${var:起始索引0:长度} # 切片,不影响原值
${var/old/new}、${var//old/new} # 替换(第一个,全部),不影响原值
${var#pattern}、${var##pattern} # 去头(最短,最长),不影响原值
${var%pattern}、${var%%pattern} # 去尾(最短,最长),不影响原值
注意:数组也可以进行切片和替换
${数组名[*]:2:2}
${数组名[*]/4/9}
${!var} 和 ${!数组名[@]} 或 ${!数组名[*]}
${!var} # 间接引用变量
${!数组名[@]}、${!数组名[*]} # 列出所有索引
# ${!var}
示例一:
]# fruit="apple"
]# apple="red"
]# echo ${fruit} # apple
]# echo ${!fruit} # red
示例二:
]# cat test.sh
#!/bin/bash
for i in `seq $#`
do
echo $i
echo ${!i}
done
]# sh test.sh a b
1
a
2
b
# ${!数组名[@]}、${!数组名[*]}
示例一:
]# test=(apple banana cherry)
]# echo ${!test[@]}
0 1 2
]# echo ${!test[*]}
0 1 2
]# cat test.sh
#!/bin/bash
aaa=111
bbb=222
ccc=333
test=(aaa bbb ccc)
for i in ${!test[@]}
do
echo ${test[i]} # ${test[0]}、${test[1]}、${test[2]}
done
echo "---"
for i in ${!test[@]}
do
echo ${!test[i]}
done
echo "---"
for i in ${!test[@]}
do
eval echo \$${test[i]} # eval将参数当做一个命令来执行,而不仅仅是作为一个字符串
done
]# sh test.sh
aaa
bbb
ccc
---
111
222
333
---
111
222
333
本文围绕Linux Shell脚本展开,介绍了Shell解释器,包括交互式与非交互式使用方式。详细阐述了脚本编写、执行方法,以及利用脚本搭建YUM、ftp、Web等服务。还讲解了变量、运算、条件测试、循环结构、函数等知识,同时介绍了正则表达式、sed、awk等工具的使用。

被折叠的 条评论
为什么被折叠?



