shell 编程
变量
定义变量
-
变量名与变量内容以一个
=
连接,且等号两边不能有空格 -
变量名称只能是英文字母、数字与下划线,但是不能以数字开头
-
变量内容若有空格可使用双引号
"
或单引号'
将变量内容组合起来- 单引号
'
为强引用,会将引号内的内容原样显示出来 - 双引号
"
为弱引用,会保持引号内特殊符号的原有特性
song=1 # 双引号保持特殊符号的原有特性 s1="i am $song" echo $s1 >i am 1 # 单引号会将引号内的内容原样显示出来 s2='i am $song' echo $s2 >i am $song
- 单引号
-
可用转意符
\
将特殊符号(如[Enter]
,$
,\
,空格符
, 等)变成一般字符 -
在一串命令中,还需要藉由其他的命令提供的信息,可以使用以下两种方式
# `命令` echo `ip a` # $(命令) 推荐 echo $(ip a)
-
增加变量的内容时,则可用以下两种方式
song=1 song=${song}55 echo $song >155
-
若该变量需要在其他子程序执行,则需要以
export
来使变量变成环境变量export song
- 使自定义的变量 成为 环境变量,环境变量可以被向下继承(子进程 仅会继承父 shell 的环境变量, 不会继承父 shell 的自定义变量),并且
export
声明的环境变量只能被其子 shell 继承使用,不能被 父 shell 继承使用
#例子 [root@kube-master py3]# bash # 打开一个子 shell [root@kube-master py3]# export a=hello # 在 子 shell 声明一个环境变量 [root@kube-master py3]# bash # 在子 shell 中再打开一个 子 shell [root@kube-master py3]# echo $a # 变量可以生效 hello [root@kube-master py3]# exit # 退出 子 shell 的 子 shell exit [root@kube-master py3]# exit # 退出 子 shell exit [root@kube-master py3]# echo $a # 在 当前 shell 中, 其子 shell 声明的环境变量是无效的 [root@kube-master py3]#
- 使自定义的变量 成为 环境变量,环境变量可以被向下继承(子进程 仅会继承父 shell 的环境变量, 不会继承父 shell 的自定义变量),并且
-
通常大写字符为系统默认变量,自定义变量可以使用小写字符,方便判断 (纯粹个人习惯) ;
-
取消变量的方法为使用
unset 变量名称
unset song
环境变量
这种变量是会影响bash环境操作的,会在真正进入系统前由一个bash程序读入到系统中。通常都环境变量的名字以大写字符命名。
1、常见的环境变量
-
PATH
系统命令存放目录 -
HOME
当前用户的家目录 -
MAIL
-
SHELL
-
PWD
当前所在目录 -
USER
当前用户 -
UID
当前用户的uid -
ID
当前用户的组id -
RANDOM
随机数 -
PS1
提示字符可用的提示字符的含义
- \d :可显示出“星期 月 日”的日期格式,如:“Mon Feb 2”
- \H :完整的主机名称。如
www.sharkyun.com
- \h :仅取主机名称在第一个小数点之前的名字,
www
- \t :显示时间,为 24 小时格式的“HH:MM:SS”
- \T :显示时间,为 12 小时格式的“HH:MM:SS”
- \A :显示时间,为 24 小时格式的“HH:MM”
- \@ :显示时间,为 12 小时格式的“am/pm”样式
- \u :目前使用者的帐号名称,如“dmtsai”;
- \v :BASH 的版本信息
- \w :完整的工作目录名称,由根目录写起的目录名称。但主文件夹会以 ~ 取代;
- \W :利用 basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
- \# :下达的第几个指令。
- \$ :提示字符,如果是 root 时,提示字符为 # ,否则就是 $
颜色表
前景 背景 颜色
30 40 黑色
31 41 红色
32 42 绿色
33 43 黄色
34 44 蓝色
35 45 紫红色
36 46 青蓝色
37 47 白色
代码 意义
0 OFF
1 高亮显示
4 underline
7 反白显示
8 不可见
设置PS1
设置颜色 [\e[F;Bm\] 其中“F”为字体颜色,编号30~37;“B”为背景色,编号40~47
取消设置 [\e[m\] 如果不取消 后面的所有内容都会是这个颜色
# 可以显示git状态的 function parse_git_dirty { [[ $(git status 2> /dev/null | tail -n1) != "nothing to commit (working directory clean)" ]] && echo "*" } function parse_git_branch { git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/git:\1$(parse_git_dirty)/" } export PS1="\[\e[36m\]○\[\e[33m\]\$(parse_git_branch)\[\e[m\] {\[\e[36m\]>\#< \[\e[34m\]\u@\h \e[33m\]\[\e[m\]}\n \[\e[31m\]\w○\$ →\[\e[m\]"
2、列出shell环境下的所有环境变量及其内容
env
是 environment (环境) 的简写,所有的环境变量(包含自定义的环境表里)set
列出系统中所有的变量,包括自定义的变量
3、bash 的环境变量文件
- longin shell
取得shell时需要完整的登入流程;特点是登入时需要用户帐号和密码 - no-login shell
取得shell时不需要再次输入帐号和密码的情况下,所得到的shell
longin shell 会读取以下两个文件:
- ·/etc/profile· :这是系统整体设定,最好不要修改
- ·~/.bash_profile
或
~/.bash_login或
~/.profile` :属于个人的配置文件
/etc/profile
会主动依序调用以下脚本文件:
/etc/inputrc
:定义快捷键/etc/profile.d/*sh
:定义bash操作接口颜色、语系、命令别名等etc/locale.conf
:定义系统的默认语系
bash 在读完 /etc/profile
后,接下来会读取以下3个文件,且只会读去一个,会按照以下顺序优先读取
~/.bash_profile //会调用 ~/.bashrc,也会有新的环境变量在下面的文件中被添加
~/.bash_login
~/.profile
最终,~/.bashrc
才是最后被读入到系统环境中的文件
让这些环境变量文件中的变量等设置及时在当前 shell
终端中生效,有下两种方式
source ~/.bashrc
或者
. ~/.bashrc
no-longin shell 当取得no-longin shell时,该shell仅会读取 ~/.bashrc 文件 而~/.bashrc最后又会调用 /etc/bashrc
/etc/bashrc
的作用:
• 依据不同的UID定义出 umask
值
• 依据不同的UID定义出提示符(就是PS1变量)
• 呼叫 /etc/profile.d/*.sh
的设定
其他的相关配置文件
• /etc/man.config
这个文件最重要的就是定义了MANPATH
这个变量,它定义了man page 的路径;在以tarball的方式安装软件时有用
• ~/.bash_history
历史命令记录文件;记录的数量与HISTFILESIZE变量有关。在/etc/profile
里
• ~/.bash_logout
记录了当我注销bash后,系统再帮我做完什么动作后才离开的。
4、预定义变量
预定义的特殊变量有着特殊的含义,用户不可以更改,所有的预定义变量都由
$
符号和另外一个符号组成
# 常用的预定义特殊变量如下
$$ 当前进程PID
$? 命令执行后的返回状态.0 为执行正确,非 0 为执行错误
$# 位置参数的数量
$* 所有位置参数的内容
$@ 显示所有的参数
$! 上一个后台进程的PID (wait命令中使用,后面讲)
5、从键盘的输入给变量赋值 read
read [-pt] 变量名
选项参数:
-p :后面可以接提示字符!
-t :后面可以接等待的『秒数!』,比如 -t 5 提示用户输入信息时间是 5 秒钟,超过 5 秒钟,程序就继续向下运行
例子:
read -p “请输入你的姓名” name
echo "你的姓名是: $name"
6、变量的删除、取代和替换
-
删除
- 从后往前删除
${变量%匹配的内容}
- 从前往后删除
${变量#匹配的内容}
- 从后往前删除
-
替换(就是设置默认值)
最常用的是
var=${str:-expr}
后面的expr就是设置的默认值
7、时间变量的计算
// 计算 3 小时之后是几点几分
date +%T -d '3 hours'
// 任意日期的前 N 天,后 N 天的具体日期
date +%F -d "20190910 1 day"
date +%F -d "20190910 -1 day"
// 计算两个日期相差天数, 比如计算生日距离现在还有多少天
d1=$(date +%s -d 20180728)
d2=$(date +%s -d 20180726)
echo $(( (d1-d2) / 86400 ))
# 输出
2
8、位置变量
位置变量也属于预定义的变量
位置变量针对于脚本来说
位置变量是根据命令出现在命令行上的位置来确定的变量,在shell调用过程中,会按参数所在的位置进行调用。
假如脚本中使用了一个位置变量,但是执行脚本的时候,传入了多个参数,程序并不会报错。但是执行的时候只传入了一个参数,同样不会报错,后面的位置参数不会被赋值
脚本基础
脚本的执行方式
- source 脚本 在父进程中执行脚本代码
- sh 脚本 在子进程中执行脚本代码,相当于打开了一个子shell,一个全新的环境
- ./脚本
set
-
set -u
- 当脚本中遇到未定义的变量时,默认是忽略。有时候这并不是开发者想要的。假如有未定义的变量,应该报错,并且终止脚本继续运行。
- 当脚本中遇到未定义的变量时,默认是忽略。有时候这并不是开发者想要的。假如有未定义的变量,应该报错,并且终止脚本继续运行。
-
set -e
-
在脚本执行中,有任何命令的返回值是非
0
的情况,则正在运行的脚本则会退出。对于一组含有管道的命令无效。# 设置之前 [root@kube-master set]# cat set-e-before.sh #!/bin/sh foo echo "继续执行" [root@kube-master set]# sh set-e-before.sh set-e-before.sh: line 3: foo: command not found 继续执行 # 设置之后 [root@kube-master set]# cat set-e-after.sh #!/bin/sh set -e foo echo "继续执行" [root@kube-master set]# sh set-e-after.sh set-e-after.sh: line 5: foo: command not found # 对于含有管道的命令无效 #!/usr/bin/env bash set -e foo | echo "shark" # 注意这里有管道符 echo "程序会继续运行" [root@kube-master set]# sh set-e.sh shark set-e.sh: line 2: foo: command not found # 这是报错信息 程序会继续运行 # foo 不是 shell 中的命令,执行会报错,但是其后面有个管道,最终管道后的 echo 命令执行成功了,这种情况下 脚本会继续执行。
-
set -o pipefail
- 需要和
set -e
配合使用。如果设置,如果管道中的所有命令都成功退出,整条命令的返回值才是0
。否则返回非0
。
默认情况下禁用此选项
# 设置 set -o pipefail 后,此时脚本就会终止运行 set -e set -o pipefail foo |echo '' echo shark [root@kube-master set]# sh set-e-pipefail.sh set-e-pipefail.sh: line 4: foo: command not found
- 需要和
基础语法
判断
-
test
测试的标制 代表的意义 判断是否存在
例子 test -e filename-e 该文件名是否存在 -f 该文件名是否存在且为文件 -d 该文件名是否存在且为目录 检测文件权限
例子 test -r filename-r 检测该文件名是否存在且可读 -w 检测该文件名是否存在且可写 -x 检测该文件名是否存在且可执行 -u 检测该文件名是否存在且具有suid属性 -g 检测该文件名是否存在且具有sgid属性 -k 检测该文件名是否存在且具有sticky bit属性 -s 检测该文件名是否存在且为非空白文件 两文件比较
例子 test file1 -nt file2-nt 判断文件1是否比文件2新 -ot 判断文件1是否比文件2旧 -ef 判断文件1与文件2是否为同一文件(inode号是否一致),可用在判断硬链接上 两个整数之间的判断
例子 test n1 -eq n2-eq 相等 -ne 不等 -gt 大于 -lt 小于 -ge 大于等于 -le 小于等于 字符串及变量判断 test -z string 判断长度是否为0,若string 为空字串,返回true test string
test !-z string判断长度是否为0,若string 为空字串,返回falseww’w test str1 == str2 判断str1是否等于str2,相等则返回true test str1 != str2 判断str1是否不等于str2,相等则返回false 在shell中以下两种情况,变量的长度均视为0
1、变量未定义
2、变量定义了,但赋值为空字符串,如a=''
多重条件判断
例如:test -r filename -a -x filename-a 和 两个条件同时成立 -o 或 两个条件有任意一个成立 ! 取反 -
判断符号[ ]
- 在中括号中的每个元素之间都需要用空格来分隔
- 在中括号内的变量最好都用双引号括起来
- 可以跟的选项与
test
一致,例如[ -f filename]
# 错误示范 # 定义变量 name="shark ops" # 开始测试值是否相等 [ ${name} == "xiguatian" ] # 报错 bash: [: too many arguments # 正确的写法 [ "${name}" == "xiguatian" ]
逻辑语句
1、if判断
# 单层
if [ 判断条件 ];then
条件成立时执行的语句
fi
# 含有else的单层结构
if [ 判断条件 ];then
条件成立时执行的语句
else
条件不成立时执行的语句
fi
# 多条件判断,elif可以有多个
if [ 判断条件1 ];then
条件1成立时执行的语句
elif [ 判断条件2 ];then
条件2成立时执行的语句
else
条件都不成立时执行的语句
fi
# 如果在判断中使用正则,则要将条件用 [[ ]] 括起来,使用 =~ 来进行匹配,如果不想匹配 则使用[[ ! 条件 ]]
#例子
#!/usr/bin/env sh
name=shark
# 匹配
if [[ "$name" =~ [a-z]+ ]];then
echo "ok"
fi
# 不匹配
if [[ ! "$name" =~ [0-9]+ ]];then
echo "good"
fi
# 嵌套,在一个 if 判断中还可以嵌套多个if判断或其他语句,只要结构完整就好
2、case语句
# 基本语法
case $变量名称 in <==关键字为 case ,还有变量前有钱字号
"第一个变量的值") <==每个变量内容建议用双引号括起来,关键字则为小括号 )
程序段
;; <==每个类别结尾使用两个连续的分号来处理!
"第二个变量的值")
程序段
;;
*) <==最后一个变量内容都会用 * 来代表所有其他值
不包含第一个变量内容与第二个变量内容的其他程序运行段
exit 1
;;
esac <==反过来写,结束当前语句结构!
# 例子,变量值不仅可以是一个,也可以是多个
#!/bin/bash
cat << EOF
m|M) show memory usages;
d|D) show disk usages;
q|Q) quit
EOF
read -p "Your choice" choice
case $choice in
m|M)
free -m
;;
d|D)
df -h
;;
q|Q)
exit
;;
*)
eco "Invalid input"
;;
esac
3、while语句
# 基础语句
while [ condition ] ==>中括号内的状态就是判断式
do ==> do 是循环的开始!
命令或者代码
命令或者其他逻辑语句
done ==> done 是循环的结束
#每次循环体执行完,while 都会检查条件是否为真,为真继续循环,否则终止循环
#所以在使用while循环时,如果不想进入无限循环,按情况添加条件
# read
# read可以同时添加多个变量
[root@sharkyun ~]# read id name age
1 shark 18
[root@sharkyun ~]# echo $age
18
[root@sharkyun ~]# echo $id $name
1 shark
# read默认分割符为空格,可以修改,IFS就是存储分割符的
[root@sharkyun ~]# old_ifs=$IFS; IFS=','
[root@sharkyun ~]# read id name age
2, xiguatian, 20
[root@sharkyun ~]# echo "$id| $name| $age"
2| xiguatian| 20
[root@sharkyun ~]# export IFS=$old_ifs
[root@sharkyun ~]# read id name age
2, xiguatian, 20
[root@sharkyun ~]# echo "$id| $name| $age"
2,| xiguatian,| 20
[root@sharkyun ~]# read id name age
2,xiguatian,20
[root@sharkyun ~]# echo "$id| $name| $age"
2,xiguatian,20| |
# while read
$ cat db.sql
1 shark 18
2 xiguatian 20
$ cat while-read-m.sh
while read id name age
do
echo "$id | $name | $age"
done < db.sql # 读取文件里的内容
$ sh while-read-m.sh
1 | shark | 18
2 | xiguatian | 20
4、for语句
for 变量 in 被循环对象
do
执行语句
done
# for 循环中变量的取值
# a. 从一组字符串中取值
for var in one two three four five
do
echo "****************************"
echo '$var is ' $var
done
# b. 从位置变量中取值
for var
do
echo '-----------------------------'
echo '$var is ' $var
done
{>15< root@song ~/test}$→sh var.sh 1 2 3 4 5
-----------------------------
$var is 1
-----------------------------
$var is 2
-----------------------------
$var is 3
-----------------------------
$var is 4
-----------------------------
$var is 5
# c. 从累计变化的格式中取值
#!/bin/bash
for ((var=1;var<=10;var++))
do
echo "------------------------"
echo '$var is ' $var
done
# d. 从命令结果中取值
#!/bin/bash
for var in $(cat file.txt)
do
echo " ------------------------"
echo '$var is ' $var
done
5、数值运算方式
# 第一种
➜ ~ n=1
➜ ~ let n++
➜ ~ echo $n
2
# 第二种
➜ ~ (( n++ ))
➜ ~ echo $n
3
# 第三种
➜ ~ a=2
➜ ~ b=3
➜ ~ let f=a+b
➜ ~ echo $n
3
➜ ~ echo $f
5
➜ ~ let f = a + b ==> 错误
zsh: bad math expression: operand expected at `='
# 第三种变式
➜ ~ let "f = a + b"
➜ ~ echo $f
5
6、break和continue
break
就是退出循环,循环不再继续了。假如是嵌套的循环,就是退出当前层级的循环。continue
是停止本次循环体内的代码,继续进行下一次循环。
1、单层循环的break
示例代码
运行结果
2、单层循环的continue
示例代码
执行结果
3、 多层循环的 break
示例代码
执行结果
数组
# 定义一个数组
var=(wukong bajie shaseng)
echo ${var[2]} //显示数组中索引号为 2 的值,索引号从 0 开始
输出 shaseng
echo ${var[*]} //显示数组中所有的值
输出 wukong bajie shaseng
# 定义数组,并且其值从命令的结果中获取
# 把文件中的每一行作为数组中的一个值
line=(`cat /etc/passwd`)
# declare 声明关联数组
# 声明
declare -A 数组名称
# 添加值
# 每次添加一个值,可以追加 var[key]=value
[shark@sharkyun ~]$ info[name]=shark
[shark@sharkyun ~]$ info[age]=18
[shark@sharkyun ~]$ echo ${info[name]} # 显示索引对应的值
shark
[shark@sharkyun ~]$ echo ${info[age]}
18
# 一次添加所以的值,不可以追加,每次都会覆盖上次的值,每个值之间使用 空格 隔开 var=([key1]="value1" [key2]="value2")
[root@kube-master script]# declare -A color
[root@kube-master script]# color=(["red"]="#ff0000", ["green"]="#00ff00")
[root@kube-master script]# echo ${color[*]}
#ff0000, #00ff00
[root@kube-master script]# color=(["a"]="#ff") # 这样会覆盖原来的值,因为这是在重新定义变量 color
[root@kube-master script]# echo ${color[*]}
#ff
[root@kube-master script]# color=(["b"]="ff")
[root@kube-master script]# echo ${color[*]}
ff
[root@kube-master script]# color[c]="#cc"
[root@kube-master script]# echo ${color[*]}
ff #cc
[root@kube-master script]# echo ${!color[*]} # 获取所有的 索引号
b c
[root@kube-master script]# echo ${#color[*]} # 统计数组中有多少个键值对
2
# 删除
[root@kube-master arry]# unset info[name]
[root@kube-master arry]# echo ${!info[*]}
age
函数
就是对代码的封装,通常会完成一个功能,而出现的一种组织和代码的方式。
- 减少代码重复编写,从而也提高了代码的可复用率。
- 程序逻辑解构清晰。
- 可以使程序代码更易读,便于管理维护。
- 是模块化编程思想的基础。
函数必须先定义才可以使用
1、定义函数
# 方法一,推荐
函数名(){
代码
}
# 方法二
function 函数名 (){
代码
}
2、调用函数
# 无参函数调用,直接使用函数名
# 有参函数调用方法,函数传参时和脚本的传参一样
函数名 参数1 参数2
# 例子
# 定义函数
say_you_say_any(){
echo "我看过很多书,但都没有 "$1" 好看^_^"
}
# 例子,调用有参函数
say_you_say_any xinyi
3、函数参数
在Shell中调用函数时可向其传递参数。在函数体内部通过 $n
的形式来获取参数的值,如:$1
表示第1个参数,$2
表示第2个参数…;当n>=10时,表示为 ${n}
,如:${10}、${11}
位置参数 | 作用 |
---|---|
$n | 利用参数向程序中传递需要调用的值n为数字,n≤9直接用数字,n≥10都需要用{}包含:$0 表示命令本身 $1-$9 表示第1-9个参数 ${10} 表示第10个参数 |
$* | 表示命令行中所有的参数,所有参数看作一个整体 |
$@ | 表示命令行中的所有参数,每个参数区分对待 |
$# | 表示命令行中所有参数的个数(不统计$0 ) |
预定义变量 | 作用 |
$? | 最后一次执行的命令所返回的状态:若变量值为0,说明上一个命令正确执行若变量值为非0,说明上一个命令执行不正确 |
$$ | 当前进程的进程号(PID) |
$! | 后台运行的最后一个进程的进程号(PID) |
# 示例
# 定义函数
show_args (){
echo "函数的第一个参数$1"
echo "函数的第二个参数$2"
echo "函数的所有参数$@"
echo '函数中 $0 还是' $0
}
# 调用函数
show_args hello world
[root@kube-master function]# sh func-args.sh
函数的第一个参数hello
函数的第二个参数world
函数的所有参数world
函数中 $0 还是 func-args.sh
4、函数中调用函数
f1(){
echo "f1...."
}
function f2(){
f1
echo "f2..."
}
f2
expect免交互工具
1、安装
yum install -y expect
2、语法结构
spawn shell 命令程序
expect "捕获到shell 命令程序执行之后输出的字符串"
send "发送给 shell 命令程序的字符串"
2.1 在命令行中使用
# 在命令行直接输入 expect 可以进入 expect 程序的解释器终端
[root@e5ac44e88027 ~]# expect
expect1.1> spawn echo "hello" # 发送一条 shell 命令, 这里是指 echo "hello"
spawn echo hello
1490
expect1.2> expect "hello" # 捕获这个字符串,只要包含这个字符串就可以
hello
expect1.3> send "yes\n" # 发送一个字符串
expect1.4> expect off # 结束这次捕获
yes
2.2 在脚本中使用
# 开始 expect 解释器程序
/usr/bin/expect<<EOF
# 设置捕获字符串后,期待回复的超时时间
set timeout 30
spawn ssh-keygen
# 开始进连续捕获
expect {
".ssh/id_rsa)" { send "\n"; exp_continue }
"Overwrite (y/n)?" { send "y\n"; exp_continue }
"no passphrase):" { send "\n"; exp_continue }
"again:" { send "\n"; exp_continue }
}
# 结束 expect 解释器程序
EOF
# ".ssh/id_rsa)" { send "\n"; exp_continue }
# 意思是 捕获到字符串 ".ssh/id_rsa)" 后 发送字符串 "\n" 就是相当于按下回车键
# exp_continue 意思是继续进行捕获,不退出 expect 程序
3、实战案例
# 写个用于自动生成密钥对的函数
auto_keygen (){
/usr/bin/expect<<EOF
set timeout 30
spawn ssh-keygen
expect {
".ssh/id_rsa)" { send "\n"; exp_continue }
"Overwrite (y/n)?" { send "y\n"; exp_continue }
"no passphrase):" { send "\n"; exp_continue }
"again:" { send "\n"; exp_continue }
}
EOF
}
# 写个自动免密登录的函数
send_key () {
pwd=upsa
/usr/bin/expect <<EOF
set timeout 30
# 发送公钥给对方服务器
spawn ssh-copy-id root@$1
expect {
"yes/no" { send "yes\n"; exp_continue }
"password:" { send "${pwd}\n"; exp_continue }
}
expect eof
EOF
}
# 定义一个变量,其值是当前用户的公钥文件
pub_key_file=$HOME/.ssh/id_rsa.pub
# 假如公钥文件不存在,说明需要创建密钥对
if [ ! -f ${pub_key_file} ];then
auto_keygen
fi
# 循环一个存放 ip 地址的文件,并且把每个 IP地址传递给 函数
for ip in $(cat ./ips.txt)
do
send_key $ip
done
操作数据库
shell 操作 MySQL 是通过给
mysql
这个客户端程序传递相应的参数实现的
mysql -u用户 -p'password' db_name -e "sql 语句"
#!/bin/bash
HOSTNAME="localhost" #数据库信息
PORT="3306"
USERNAME="root"
PASSWORD="QFedu123!"
DBNAME="d1" #数据库名称
TABLENAME="t1" #数据库中表的名称
exec_mysql="mysql -h${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD}"
#创建数据库
create_db_sql="create database IF NOT EXISTS ${DBNAME}"
${exec_mysql} -e "${create_db_sql}"
${exec_mysql} "show databases;"
#创建表
create_table_sql="create table IF NOT EXISTS ${TABLENAME} ( name varchar(20), id int(11) default 0 )"
${exec_mysql} ${DBNAME} -e "${create_table_sql}"
#插入数据
insert_sql="insert into ${TABLENAME} values('billchen',2)"
${exec_mysql} ${DBNAME} -e "${insert_sql}"
# 查询
# 查询时候可能需要避免不必要的输出
search_dbs="show databases;"
${exec_mysql} ${DBNAME} -e "${search_dbs}" -N -B
# -N 不输出列名(字段名)
# -B 不输出数据之间的边框竖线 (|)
# 输出格式可以是其他的,比如 -H 输出 HTML 格式
# 隐藏用户名和密码
[mysql]
user=root
password='QFedu123!'
mysql --defaults-file=./mysql_pwd -h 172.17.0.2 -e "show databases;" -N -B
正则和grep
怎么说呢?正则这个玩意,必须得会!!
1、定义及使用范围
正则表达式基本上是一种“表达式”, 只要工具程序支持这种表达式,那么该工具程序就可以用来作为正则表达式的字串处理之用。 例如 vi
, grep
, awk
,sed
等等工具,因为她们有支持正则表达式, 所以,这些工具就可以使用正则表达式的特殊字符来进行字串的处理。但例如 cp, ls 等指令并未支持正则表达式, 所以就只能使用 Bash 自己本身的通配符而已。
下面用到的模板
cat test.txt
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.
Football game is not use feet only.
this dress doesn't fit me.
However, this dress is about $ 3183 dollars.
GNU is free air not free beer.
Her hair is very beauty.
I can't finish the test.
Oh! The soup taste good.
motorcycle is cheap than car.
This window is clear.
the symbol '*' is represented as start.
Oh! My god!
The gd software is a library for drafting programs.
You are the best is mean you are the no. 1.
The world <Happy> is the same with "glad".
I like dog.
google is the best tools for search keyword.
goooooogle yes!
go! go! Let's go.
# I am VBird
2、基础
[:alnum:] 代表所有的大小写英文字符和数字 0-9 A—Z a-z
[:alpha:] 代表任意英文大小写字符 A-Z a-z
[:lower:] 代表小写字符 a-z
[:upper:] 代表大写字符 A-Z
[:digit:] 代表数字 0-9
3、入门匹配
^world 匹配以world开头的字符串
# 例子 匹配以#开头的行
grep '^#' test.txt
world$ 匹配以world结尾的字符串
# 例子 匹配以!结尾的行
grep '!$' test.txt
. 代表任意的一个字符
# 例子 匹配两个e之间有任意一个字符的行
grep 'e.e' test.txt
\ 跳脱字符,将特殊符号的特殊意义去除 # 特殊符号有 '(单引号) " (双引号) .(英文句号)
# 例子 匹配含有单引号'的行
grep \' test.txt
# 例子二 匹配含有英文句号.的行
grep '\.' test.txt
* 代表零到无穷多个 前一个字符 # 因为*为重复前一个字符,所以在*之前必须有一个字符!
# 想要匹配任意字符可以这样 .*
# 根据下面这个例子可以看出 s* 为一个整体 即*于它前面的字符是一个整体出现在匹配规则中
# 如你想要匹配到 es后面不知道多少s 就必须写 ess* 如果是es* 则会匹配到 单独的e
# 例子 匹配 前面为 es 后面多个s的行
grep 'ess*' test.txt
[list] 字符集合,列出了可选的字符,仅代表包含列表其中一个字符
# 匹配a[afi]y 就是匹配 aay afy aiy
# 例子 匹配gl 或者gd
grep 'g[ld]' test.txt
[n1 - n2] 同上,列出的是一个连续的范围
# 例子 匹配含有数字0-9的行
grep '[0-9]' test.txt
[^list] 反向选择,列表中的是不会出现的字符
# 例子 匹配不含有ood 的行
grep 'oo[^d]' test.txt
\{n,m\} 连续n到m个前面的字符 # 用法同*
# 如果是 \{n\} 是连续n个前面的字符
# 如果是 \{,m\} 是0到m个前面的字符
# 例子 匹配以g开头 d 结尾 中间两个到三个o 的行
grep 'go\{2,3\}d' test.txt
# 例子二 匹配连续两个o 的行
grep 'o\{2\}' test.txt
# 例子三 匹配0到2个连续的o
grep 'o\{,2\}' test.txt
4、扩展正则
支持扩展正则的工具
grep -E
、egrep
、sed
、awk
grep
如果要支持扩展正则 需要加-E
或者直接使用egrep
+ 重复一个或一个以上前一个字符
例子
egrep 'go+d' test.txt
? 零个或一个前面的字符
例子
egrep 'go?d' test.txt
| 或
例子 找出gd 或者 good
egrep 'gd|good' test.txt
() 群组
例子:找到glad 或者 good
egrep 'g(la|oo)d' test.txt
()+ 多个重复群组
例子: 找以A开头 C 结尾 ,中间有一个以上的xyz
egrep 'A(XYZ)+C' test.txt
# 还有一些比较晦涩的东西,自己悟一悟
echo "2019-12-30" |grep -E '[1-9][0-9]{3}-((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[01]))'
2019-12-30
echo "1919-12-30" |grep -E '[1-9][0-9]{3}-((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[01]))'
1919-12-30
#第一点 {数字} 可以指定前面东西出现的次数
5、贪婪匹配
grep 默认贪婪
如果想实现非贪婪需要加上
-P
,这会使用Perl
语言环境的正则
贪婪 就是尽可能的多匹配
非贪婪 就是尽可能的少匹配,只需要在一些表示量词(就是次数)的后面加上 ?, 比如: .*? +?
例子
Perl 语言中:
\w
表示任意一个
大小写字母[a-zA-Z]
、下划线_
和数字[0-9]
\d
表示任意一个
数字[0-9]
当然这些规则适用于大部分的 编程语言,比如 python java javascript
awk
一、awk简介
awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。
数据可以来自标准输入、一个或多个文件,或其它命令的输出。
支持用户自定义函数和动态正则表达式等先进功能,是linux/unix
下的一个强大编程工具。
可以在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。
awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。
gawk是awk的GNU版本,它提供了Bell实验室和GNU的一些扩展。
二、awk语法
# 第一种
awk [options] 'commands' filenames
# 第二种
awk [options] -f awk-script-file filenames
# 解释
# options
-F 对于每次处理的内容,可以指定一个子定义的分隔符,默认的分隔符是空白字符(空格或 tab 键 )
# command
BEGIN{} 处理所有内容之前的动作,通常用于定义一些变量,例如 BEGIN{FS=":";OFS="---"}
{} 处理内容中的动作
END{} 处理所有内容之后的动作
# 示例
awk 'BEGIN{print "----开始处理了---"} {print "ok"} END{print "----都处理完毕---"}' /etc/hosts
----开始处理了---
ok
ok
ok
----都处理完毕---
三、awk工作原理
(1)awk,会处理文件的每一个行,每次处理时,使用一行作为输入,并将这一行赋给内部变量$0,每一行也可称为一个记录,以换行符结束
(2)然后,行被**😗*(默认为空格或制表符)分解成字段(或称为域),每个字段存储在已编号的变量中,从$1开始,
最多达100个字段
(3)awk如何知道用空白字符来分隔字段的呢? 因为有一个内部变量FS来确定字段分隔符。初始时,FS赋为空白字符
(4)awk打印字段时,将以内置的方法使用 print
函数打印,awk 在打印出的字段间加上空格。这个空格是内部的一个变量 OFS
输出字段的分隔符, 逗号 ,
会和 OFS
进行映射,通过 OFS
可以控制这个输出分隔符的值。
(5)awk输出之后,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程将持续到所有行处理完毕
四、记录与字段相关的内部变量
变量 | 作用 |
---|---|
$0 | awk变量 $0 保存当前正在处理的行内容 |
NR | 当前正在处理的行是 awk 总共处理的行号。 |
FNR | 当前正在处理的行在其文件中的行号。 |
NF | 每行被处理时的总字段数 |
$NF | 当前处理行的分隔后的最后一个字段 的值 |
FS | 输入行时的字段分隔符,默认空格 |
OFS | 输出字段分隔符,默认是一个 空格 |
ORS | 输出记录分隔符, 默认是换行符. |
五、格式化输出printf
awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd
awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
格式 | 含义 |
---|---|
%s | 字符类型 |
%d | 十进制整数 |
%f | 浮点数类型 |
%-15s | 占15字符- 表示左对齐,默认右对齐 |
printf | 默认不会再行尾自动换行,加\n |
六、awk模式和动作
任何 awk
语句都由 模式 和 动作 组成。模式部分
决定动作语句何时触发及触发事件。如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句或复合语句或正则表达式。
模式可以是
-
正则表达式
# 将整行进行正则匹配(包含) # 就是当前处理的行有没有包含 指定的模式(书写的正则表达式) /正则/ 正则需要写在双斜线内 # 示例 awk '/^root/' /etc/passwd awk '$0 ~ /^root/' /etc/passwd awk '!/root/' /tec/ passwd awk '$0 !~ /^root/' /etc/passwd # 将某一字段进行正则匹配 # 可以使用的匹配操作符(~ 和 !~) 字段 ~ /正则/ # 示例 awk -F: '$1 ~ /^alice/' /etc/passwd awk -F: '$NF !~ /bash$/' /etc/passwd # 实现 字符串的完全相等需要使用 == # 字符串需要使用双引号 != 表示不等于 # 示例 awk -F: '$NF == "/bin/bash"' /etc/passwd awk -F: '$1 == "root"' /etc/passwd # 比较表达式 # 比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。比较表达式使用关系运算符,用于比较数字与字符串。 # 关系运算符有 # < 小于 例如 x<y # <= 小于或等于 x<=y # > 大于 x>y # >= 大于等于 x>=y # == 等于 x==y # != 不等于 x!=y # 示例 awk -F: '$3 == 0' /etc/passwd awk -F: '$3 < 10' /etc/passwd df -P | grep '/' |awk '$4 > 25000' # 条件表达式 # 示例 awk -F: '$3>300 {print $0}' /etc/passwd awk -F: '{ if($3>300) print $0 }' /etc/passwd awk -F: '{ if($3>300) {print $0} }' /etc/passwd awk -F: '{ if($3>300) {print $3} else{print $1} }' /etc/passwd # 算术运算:+, -, *, /, %(模: 取余), ^(幂:2^3) # 可以在模式中执行计算,awk都将按浮点数方式执行算术运算 # 示例 awk -F: '$3 * 10 > 500' /etc/passwd awk -F: '{ if($3*10>500){print $0} }' /etc/passwd # 逻辑操作符和复合模式 # && 逻辑与, 相当于 并且 # ||逻辑或,相当于 或者 # ! 逻辑非 , 取反 # 示例 awk -F: '$1~/root/ && $3<=15' /etc/passwd awk -F: '$1~/root/ || $3<=15' /etc/passwd awk -F: '!($1~/root/ || $3<=15)' /etc/passwd # 范围模式, 模式之间用逗号 , 隔开 # 使用语法是: 起始表达式, 终止表达式 # 示例 下面的意思是: 从开头是 bin 的行开始匹配成功一直到含有 adm 的行结束匹配,也就是 开头是 bin 的行到含有 adm 的行 的所有内容都符合匹配条件。 ➜ ~ awk -F: '/^bin/,/adm/ {print $0 }' /etc/passwd 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 # 生产实例 # 统计日志中某个时间范围的 IP 访问量,并进行排序 # 部分日志 110.183.58.144 - - [10/May/2018:23:49:27 +0800] "GET http://app.znds.com/html/20180504/y222sks_2.2.3_dangbei.dangbei HTTP/1.1" 200 14306614 "-" "okhttp/3.4.1" 1.69.17.127 - - [10/May/2018:23:49:31 +0800] "GET http://app.znds.com/down/20180205/ttjs_3.0.0.1_dangbei.apk HTTP/1.1" 200 13819375 "-" "okhttp/3.4.1" 1.69.17.127 - - [10/May/2018:23:49:40 +0800] "GET http://app.znds.com/down/20180416/ttyj_1.1.6.0_dangbei.apk HTTP/1.1" 200 16597231 "-" "okhttp/3.4.1" 1.69.17.127 - - [10/May/2018:23:50:00 +0800] "GET http://app.znds.com/down/20170927/jydp_1.06.00_dangbei.apk HTTP/1.1" 200 36659203 "-" "okhttp/3.4.1" # 具体实现 $ start_dt='10/May/2018:23:47:43 $ end_dt='10/May/2018:23:49:05' $ awk -v st=${start_dt} -v ent=${end_dt} -F'[][ ]' '$5 == st,$5 == ent {print $1}' app.log |sort |uniq -c |sort -nr |head -n 10 66 223.13.142.15 6 110.183.13.212 4 1.69.17.127 1 113.25.94.69 1 110.183.58.144 # awk正则示例 # 匹配开头是 bin 的或者开头是 root 的行 ➜ ~ awk -F: '/^(bin|root)/' /etc/passwd root:x:0:0:root:/root:/bin/zsh bin:x:1:1:bin:/bin:/sbin/nologin
# 指定多个分割符号 # 使用[] # 示例 ➜ ~ echo "a b|c d| ||||e | |" |awk -F'[ |]' '{print $10}' e ➜ ~ echo "a b|c d| ||||e | |" |awk -F'[ |]+' '{print $5}' e awk -F'[][ ]+' '$4 ~ /^11\/Apr\/2020/, $4 ~ /^17\/Apr\/2020/ && $7 ~ /^\/star\/local\/app/ {j++}END{print "总访问量:"j,"平均:"j/7}' access.log # 注意: 中括号内的任意字符均视为普通字符, 比如 . * 都被看做是 普通字符。 # 例如: $ echo "a.b*c" |awk -F'[.*]' '{print $1, $2,$3}' a b c
七、awk脚本编程
if语句
# 格式 { if (表达式) {语句; 语句; ...}}
awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd
输出: root is administrator.
# 统计系统级别用户的数量
awk -F: '{if($3>0 && $3<1000){count++;}} END{print count}' /etc/passwd
输出: 22
if…else语句
# 格式 {if(表达式){语句;语句;...}else{语句;语句;...}}
awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd
awk -F: '{ if($3==0){count++} else{i++} } END{print "管理员个数: "count "系统用户数: "i}' /etc/passwd
输入:
管理员个数: 1系统用户数: 24
awk -F: '{ if($3==0){count++} else{i++} } END{print "管理员个数: "count ; print "系统用户数: "i}' /etc/passwd
输出:
管理员个数: 1
系统用户数: 24
if…else if…else语句
# 格式
# {if(表达式1) {语句;语句;...} else if (表达式2) {语句;语句;...} else if(表达式3){语句;语句;...} else {语句;语句;...} }
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print i; print k; print j}' /etc/passwd
输出:
1
2
22
awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理员个数: "i; print "普通用个数: "k; print "系统用户: "j}' /etc/passwd
输出:
管理员个数: 1
普通用个数: 2
系统用户: 22
八、awk使用外部变量
方法一:awk参数-v(推荐使用,易读)
[root@sharkyun ~]# w=hello
[root@sharkyun ~]# echo "hello world" | awk -v var=$w '$1 == var {print $1}'
hello
方法二:管道符
[root@tianyun ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/cl-root 2.8T 246G 2.5T 9% /
tmpfs 24G 20K 24G 1% /dev/shm
/dev/sda2 1014M 194M 821M 20% /boot
[root@tianyun ~]# df -h |awk '{ if(int($5)>5){print $6":"$5} }'
/:9%
/boot:20%
[root@tianyun ~]# i=10
[root@tianyun ~]# df -h |awk '{ if(int($5)>'''$i'''){print $6":"$5} }'
/boot:20%
sed
一、sed工作流程
sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。
处理时,先把当前处理的行内容存储在临时缓冲区中,称为“模式空间”(pattern space),
之后再用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容打印到屏幕。
接着处理下一行,这样不断重复,直到文件末尾。
注意:模式空间的内容和 AWK 中的 $0 是一样的,处理每行的时候,都会被重新赋值为当前行的内容
文件内容并没有改变,除非你使用重定向存储输出。
Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
二、命令格式
# 处理单个文件
sed [options] '[匹配模式] sed 的内部命令' file1
# 处理多个文件
sed [options] '[匹配模式] [sed 的内部命令]' file1 file2
# options 选项是可选的,意思就是没有也行
-r 支持正则
-i 修改
-n 取消默认输出
-e 多重编辑
# 匹配模式 是可选的用于在文件中每一行进行匹配到模式,模式可以是正则,也可以是文件的行号
# 内部的命令也是可选的,没想到吧,但是两个单引号是必须的
s 替换
a 追加
d 删除
i 插入
c 修改
# 注:sed和grep不一样,不管是否找到指定的模式,它的退出状态都是0。只有当命令存在语法错误时,sed的退出状态才是非0
三、基础应用
# 支持正则
# 与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是括在斜杠间的模式,用于查找和替换,以下是sed支持的元字符。
# 使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\),\{\}
# 使用扩展元字符集 ?, +, { }, |, ( )
# 使用扩展元字符的方式:
sed -r
# 在实际使用的时候,都会加上 -r 参数,即使没有用的扩展正则也不会有任何影响。
# 输出内容
# sed 默认会输出文件的每一行,无论这行内容是否能匹配上匹配模式,假如被匹配到的则会再输出一次。
sed -r '' mypasswd
> root:x:0:0:root:/root:/bin/bash
> bin:x:1:1:bin:/bin:/sbin/nologin
> daemon:x:2:2:daemon:/sbin:/sbin/nologin
sed -r 'p' mypasswd
> root:x:0:0:root:/root:/bin/bash
> root:x:0:0:root:/root:/bin/bash
> bin:x:1:1:bin:/bin:/sbin/nologin
> bin:x:1:1:bin:/bin:/sbin/nologin
> daemon:x:2:2:daemon:/sbin:/sbin/nologin
> daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 这里的p并不是匹配p字符,而是sed的内部命令,有打印(输出)的作用
# 使用-n 可以屏蔽掉默认输出
sed -rn 'p' mypasswd
> root:x:0:0:root:/root:/bin/bash
> bin:x:1:1:bin:/bin:/sbin/nologin
> daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 显示包含root的行
sed -rn '/root/p' mypasswd
# 匹配root 开头的行
sed -rn '/^root/p' mypasswd
# 搜索替换
# 搜索每一行,找到有 root 的,把第一个替换为 shark
sed -r 's/root/shark/' mypasswd
# 搜索每一行,找到所有的 root 字符,进行全局替换为 `shark`
sed -r 's/root/shark/g' mypasswd
# 忽略大小写
sed -r 's/root/shark/gi' mypasswd
# 删除
# 找到含有 root 的进行删除
sed -r '/root/d' mypasswd
# 改变界定符号
# 可以使用不同的 字符 作为界定符号,注意进行转义
sed -r '\#root#d' mypasswd
# 注意:当在模式匹配中使用其他界定符号时,需要对其进行转义。其他界定符用在 s 搜索替换时不必转义。
# 例如:
sed -r 's#root#shark#' mypasswd
sed -r 's%root%shark%' mypasswd
sed -r 's|root|shark|' mypasswd
# 地址(定址)
# 地址用于决定对哪些 行 进行编辑。地址形式可以是数字、正则表达式或二者的结合。如果没有指定地址,sed将处理输入文件中的所有行。
# 全部分删除
sed -r 'd' mypasswd
# 第 3 行删除
sed -r '3 d' mypasswd
# 第 1 行到第 3 行都删除
sed -r '1,3 d' mypasswd
# 含有 root 字符串的行删除
sed -r '/root/ d' mypasswd
# 从含有 root 字符串的行开始匹配,一直删除到第 5 行
sed -r '/root/,5 d' mypasswd
# 从含有 halt 的行开始删除,并删除此行之后的 2 行,就是总共删除 3 行
sed -r '/halt/,+2 d' mypasswd
# 含有 root 的行不删除,其他都删除
sed -r '/root/ !d' mypasswd
# 使用行号除以 2 ,余数是 1 的行删除
sed -r '1~2 d' mypasswd
# 使用行号除以 2, 余数 是 0 的 打印出来
sed -rn '0~2 p' mypasswd
# 试试下面这个, 就是 每次处理的行号是被除数,第二个数是除数,第一数是 余数
sed -rn '0~3 p' mypasswd
四、匹配模式和内部命令
# 替换 s
# //&代表在查找串中匹配到的所有内容,如果没有显示,可以加一个p
sed -rn 's/[0-9][0-9]/&=5/' mypasswd
# 效果
> operator:x:11:0:operator:/root:/sbin/nologin
> operator:x:11=5:0:operator:/root:/sbin/nologin
# 将前面的no分组,组号为1,然后将nologin替换为no不可登录
sed -r 's/(no)login/\1不可登录/' mypasswd
# 追加 a
sed -r '$a 1.1.1.1 www.qfedu.com' /etc/hosts
# 如果不加$ 会在每一行的下面追加一次
# 插入 i
# 在第二行后面插入
sed -r '2i\1111111111111' /etc/hosts
# 插入中的换行在插入后也生效
sed -r '2i111111111\
> 2222222222\
> 3333333333' /etc/hosts
# 修改 c
# 将第二行修改为111111111111
sed -r '2c\1111111111111' /etc/hosts
sed -r '2c\111111111111\
> 22222222222\
> 33333333333' /etc/hosts
五、sed常用的操作
# 删除配置文件中 # 号注释的行
sed -ri '/^#/d' file.conf
# 删除开头的一个或者多个空格或者 Tab 键 加上 '#' 或者开头是 '#' 的行
sed -ri '/^[ \t]*#/d' file.conf
# YUM 源修改
sudo sed -ri s/^#baseurl/baseurl/g /etc/yum.repos.d/CentOS-Base.repo
sudo sed -ri s/^mirrorlist/#mirrorlist/g /etc/yum.repos.d/CentOSBase.repo
# 删除配置文件中//号注释行
sed -ri '\#^[ \t]*//#d' file.conf
# 删除无内容空行
# - 开头和结尾之间什么都没有的行
# - 开头和结尾之间有多个空格的行
# - 开头和结尾之间有多个 Tab 键的行
sed -ri '/^[ \t]*$/d' file.conf
# 删除注释行及空行:
# 以下 3 中效果一样,挑一个自己喜欢的
# 使用;分割两个条件
sed -ri '/^[ \t]*#/d; /^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
# 使用| 分割两个条件
sed -ri '/^[ \t]*#|^[ \t]*$/d' /etc/vsftpd/vsftpd.conf
# 使用(|)分割两个条件
sed -ri '/^[ \t]*($|#)/d' /etc/vsftpd/vsftpd.conf
# 修改文件:
sed -ri '$a\chroot_local_user=YES' /etc/vsftpd/vsftpd.conf
sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config
sed -ri '/UseDNS/cUseDNS no' /etc/ssh/sshd_config
sed -ri '/GSSAPIAuthentication/cGSSAPIAuthentication no' /etc/ssh/sshd_config
# 给文件行添加注释:
sed -r '2,6s/^/#/' a.txt
# 使用小括号进行分组,可以有多个分组, 后面可以使用 \1 获取到第一个分组的内容
sed -r '2,6s/(.*)/#\1/' a.txt
# &匹配前面查找的内容
sed -r '2,6s/.*/#&/' a.txt
# 将行首零个或多个#换成一个#
sed -r '3,$ s/^#*/#/' a.txt
sed -r '30,50s/^[ \t]*#*/#/' /etc/nginx.conf
sed -r '2,8s/^[ \t#]*/#/' /etc/nginx.conf
# sed中使用外部变量
var1=11111
# 无效
sed -r '3a$var1' /etc/hosts
# 正确
sed -r "3a$var1" /etc/hosts
# 有效
sed -r 3a$var1 /etc/hosts
# 报错
sed -r "$a$var1" /etc/hosts
# 有效,但是中间不能有空格
sed -r '$a'"$var1" /etc/hosts
# 有效, 将第一个 $ 进行转义
sed -r "\$a $var1" /etc/hosts
# 多重编辑选项:-e
sed -e '1,3 d' -e 's/root/shark/' mypasswd
4:adm:x:3:4:adm:/var/adm:/sbin/nologin
5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6:sync:x:5:0:sync:/sbin:/bin/sync
7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8:halt:x:7:0:halt:/sbin:/sbin/halt
9:mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10:operator:x:11:0:operator:/shark:/sbin/nologin
# 等同于
sed '1,3 d; s/root/shark/' mypasswd