详解shell三剑客
县长在鹅城,让我教点真的!
内容详细,有例子有截图,需要静下心看,就可以掌握很好。
文章目录
学习前了解
管道命令 |
使用“|”可以将两个命令分隔开,“|”左边命令的输出作为“|”右边命令的输入,可连续使用
cat demo.sh | grep ip | sort | uniq
重定向功能 > 和>>
将返回值重定向,如写入某个文件,若此文件不存在则自动创建
echo, awk, grep 等等具有输出的都可进行此操作
> # 覆写,覆盖文件原本内容
>> # 续写,在文件末尾添加
三剑客 grep , awk, sed
都支持基本正则表达式,参考文章最后正则表达式讲解。
1.grep
1.1语法
# 基本用法, grep [选项] 目标值 文件(可以多个文件)
grep [options] pattern [files]
# 配合管道用法
cat demo.txt | grep [options] pattern
1.2参数详解
| 选项参数 | 作用 | 使用样例 |
|---|---|---|
| -i | 忽略大小写 | grep -i daTa ./osp.txt |
| -v | 返回不匹配的 | grep -v usrId ./osp.txt |
| -n | 显示行号 | grep -n id ./osp.txt |
| -c | 计数用,只打印匹配的行数 | grep -c id ./osp.txt |
| -w | 整词匹配 | grep -w water ./osp.txt |
| -r | 递归处理子目录的文件 | grep -r www ./ |
| -l | 处理多个文件时使用,只打印有匹配项的文件名 | grep -l test.txt test2.txt |
| -f | 从文件中读取多个pattern规则来过滤目标文件 grep -f 可以直接使用 fgrepcat | cat test.log | grep -f pattern.config |
| -e | 小写e,参数为pattern匹配规则,可以连续使用。 默认一个pattern时,无需添加 每一个-e都可以单独配合其他选项处理。 多个规则时,或的关系,只要满足规则其中一个就会打印 | grep -ie ‘www’ -ne ‘monkey.com’ ./web.js |
| -E | 大写E,启用扩展表达式,详细参考末尾正则表达式讲解 | grep -E |
# 多个pattern
grep -e 'www' -ie 'Monkey.COM' ./web.js
# 以下是多文件匹配时参数,多个文件处理,三个例子
# 匹配规则pattern加不加引号都可
grep 'os' demo1.txt demo2.txt
cat demo1.txt demo2.txt | grep 'os'
grep -r 'os' ./
# 以下是从多规则(pattern)匹配,满足其中一个规则就返回
-f # 文件中读取多个关键词
# grep -f 可以直接使用 fgrep
cat demo1.txt | grep -f demo2.txt# 举例:demo2提取关键词,处理demo1内容
cat demo1.txt | fgrep demo2.txt
-E # 大写E,扩展正则表达式
# grep -E 等同于 egrep
cat demo.txt | grep -E 'o.p'
cat demo.txt | egrep 'o.p'
例子文件内容如下:

- 整词匹配 -w

- 输出行号,输出不匹配项,通配符多文件处理

- 递归处理子文件夹文件 -r , 只输出文件名 -l

- 从文件读取需要匹配的关键词来筛选文件

2.awk
2.1语法
1.常用简易语法: awk options ’ [pattern] {action}’ file
options为选项,例如-F,-e
pattern不必填,匹配规则,设定被处理的行,和grep,sed的pattern一个作用
{action} 执行的操作
# 样例文件内容
[monkey@osp~01] /home/monkey> cat web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=张麻子
# 打印包含add的行=右边的值
# 解析:pattern 匹配包含add的行, 分隔符 = , acction 打印第二项
[monkey@osp~01] /home/monkey> awk -F '=' '/add/ {print $2}' web
www.monkey.com
http://127.0.0.1:80
https://monkeys.cn
# pattern种可以使用$1,$2等内置变量,参考2.2内置变量
# 打印id的值
# pattern 匹配$1等于id的,分隔符 = , action 打印第二项
[monkey@osp~01] /home/monkey> awk -F '=' '$1 == "id" {print $2}' web
1
[monkey@osp~01] /home/monkey>

- 标准语法: awk options ‘BEGIN {action} [pattern] {action} END {action}’ file
只是比简易的多了BEGIN和END的操作。
# 设置分隔符为 = , 打印=右边的值;;结果如图
awk -F '=' 'BEGIN {printf("开始了\n")} {print $2} END{printf("结束了\n")}' web
# 分隔符设置可以放在BEGIN的action中
# !!!注意,赋值时=两边不要加空格,且用双引号,{}中写多个语句用;隔开
#;;结果如图
awk 'BEGIN {printf("开始了\n");FS="="} {print $2} END{printf("结束了\n")}' web
# 使用pattern设定处理的行
## 只打印包含add的;;结果如图
awk 'BEGIN {printf("开始了\n");FS="="} /add/ {print $2} END{printf("结束了\n")}' web


语法解析:
@1
操作 file; pattern规则选定对应行,对选定行逐行处理;循环每行进行action操作
# pattern为空,则对每一行都执行print "hello"
awk '{print "hello"}' demo.sh
# 逐行执行操作:词句就会打印n条“hello”,n为行数

@2
操作file; ‘BEGIN {action}’ 处理文件前执行的代码块,包括定义变量等,可代替@1中options操作; [pattern] {action}与@1相同,选定行执行操作;'END {action}'文件所有行处理完后执行的代码块。
使用方法:
直接使用 awk ... file
配合管道 ... | awk ...
2.2内置变量
| 内置变量 | 含义 |
|---|---|
| $0 | 整行内容 |
| $1, $2…, $n | 当前在处理行分割后第1,2…,n块内容 |
| NF | 当前在处理行分割后块个数 |
| NR | 当前在处理的行号,多文件处理时,行号接续 |
| FNR | 多个文件处理时,单独处理各个文件的行号(每个文件都从0开始) 多文件处理写法只需要往后边加其他文件,空格隔开 awk ‘{print $0}’ demo1.txt demo2.sh demo3.json |
| FS | 字段分隔符,默认空格或tab |
| RS | 行分隔符,默认回车 |
| OFS | 返回当前字段分隔符 |
| ORS | 返回当前行分隔符 |
| FILENAME | 返回当前文件名(开始前,即BEGIN语句块中读不到) |
| ARGC | 命令行参数个数 |
| ARGV | 命令行参数数组 |
2.3参数详解
# options - 参数
-F <分隔符> 或 --field-separator=<分隔符># 选定分隔符,默认是空格
-v <变量名>=<值> # 设置awk内部变量值
-f <awk脚本> # 加载指定awk文件
-v 或 --version # 显示awk版本信息
-h 或 --help # 帮助
# pattern - 全文中选定行,默认所有,类似grep
运算符或正则表达式
$1 == "DNS3"
# action - 行中选定对应内容块,默认为整行;printf 参照C语言
$0 整行, $1 第一个内容块, 以此类推
{print $1} # 输出结果: 苹果
printf("%s有%d个", $1, NR)
# %s字符串变量替换,%d十进制数字变量替换,输出结果: 苹果有10个
# BEGIN {action} 开始处理文件前执行一遍action
# pattern{action} 对pattern条件选中的行循环,每行执行一次该action
# END {action} 文件处理过后执行一遍action
awk 'BEGIN {print "开始处理文件..."; FS=":"} {print $1 $2} END {printf("处理结束,共处理%d行\n",NR)}' demo.sh
# 关于END 和 NR结合使用,NR返回当前处理行的行号,而END时已经处理完毕,只执行一遍END包裹代码块,可用来返回文件行数,写在中间的逐行处理代码块里,就会是打印每行行号“1 2 3 4...”

2.4拓展
pattern
回忆一下pattern是干嘛的?逐行处理前,筛选对应行先!

可以使用NR等内置变量,在匹配项这里不用加$
可以使用比较运算==,>,<,>=,<=
可以使用逻辑运算&& || !
可以使用算术运算 + - * / %
可使用正则表达式,参考文件末尾 4.正则表达式
# == 相等
## 打印$1等于id的值
[monkey@osp~01] /home/monkey> awk -F '=' '$1 == "id" {print $2}' web
1
# ~ 包含
## 打印$1包含add的值
[monkey@osp~01] /home/monkey> awk -F '=' '$1 ~ "add" {print $2}' web
www.monkey.com
http://127.0.0.1:80
https://monkeys.cn
# !~ 不包含
[monkey@osp~01] /home/monkey> awk -F '=' '$1 !~ "add" {print $2}' web
1
张麻子
# && 包含monkey且包含http
[monkey@osp~01] /home/monkey> awk -F '=' '$2 ~ "monkey" && $2 ~ "http" {print $2}' web
https://monkeys.cn
# NR 内置变量,筛选行号大于3
[monkey@osp~01] /home/monkey> awk -F '=' 'NR > 3 {print $2}' web
1
张麻子

2.5语句块
printf打印格式

和C语言printf一样,这里举四种用法,了解其他的搜’C语言输出’即可
"%s" #字符串
"%d" #十进制数
# 给出十个位置打印字符串,若$1超出10位,则原样输出,若未超出,则在前边补空格,若加个负号就是往后补空格
printf("%10s", $1)
printf("%-10d", $2)
# 给出3位置,缺位往前补0(只对%d有效),.3和03效果一样,加负号对数字无效果(不然就改变数字本来的值了)
printf("%03d", $2)
printf("%.3d", $2)
printf("%-.3d", $2)


3.sed
文本编辑器,功能强大,还可以配合正则使用。
说明:为了方便学习,这里的每一个功能都会配一个例子
先学一个简单的替换操作,方便理解后边的例子
# 替换操作在表达式中用s
sed 's/源文本/新文本/选项' 文件
选项先不用管,用g

3.1.两个样例文件
config.json
//测试文本
{
"testData":"abcde",
"testId": "12"
}
config.ini
testData=abcde
testId=12
准备工作完毕,开始学习
3.2.语法
和awk,grep一样,主要分为两种,一种文件处理,一种读取输出
# 1.表达式(脚本)
sed [选项] '表达式(脚本)' 文件...
# 2.读取标准输出
cat test.sh | grep sed [选项] 脚本
- sed默认是更改读取出来的一个文件缓存,不会改动原文件
- sed命令中的文件可以是多个
# 多个文件,全文的test替换为osp
sed 's/test/osp/g' config.json config.ini

- 当sed要处理的文本内容包含/,需要使用 | 作为分隔符,防止冲突
# 要处理的内容包含/,需要使用 | 进行区分
# 把输出错误的h/hom/onkey改为/home/monkey
echo /hom/onkey/osp/nanny | sed 's|/hom/onkey|/home/monkey|g'

3.3.参数详解
-n, --quiet, --silent 静默模式
sed -n 's/test/osp/g' config.json
作用: sed处理后文本不再在命令行输出
- 可以配合脚本选项p,打印被处理行
sed -n 's/test/osp/p' config.json

-e 指定执行脚本, --expression=脚本
sed -e 's/test/osp/g' config.json
sed --expression='s/test/osp/g' config.json
作用:指定之后的值为脚本(表达式),而不是存储脚本集的文件
- sed默认就是-e,读取脚本(表达式)进行处理
所以只需一个表达式时,无需写-e
例子:
sed命令可以通过-e使用多个脚本
# 先把test替换为osp,再把Id替换为User
sed -e 's/test/osp/g' -e 's/Id/User/g' config.json

这里有一个比较不能忽视的点,-e并不是从“串行”的,不是对前边的-e脚本的结果进一步处理,而是“并行”,似乎是一起处理的
看例子:
-e删除两次第三行,预想结果:删掉了testData行,第二次再删第三行会删掉testId行,最后结果是空{}
但结果不是

这个涉及到sed的处理原理:
不是并行也不是串行
sed 会把文件逐行读取到模式空间,对每一行进行操作
一行操作完后,再操作下一行。
//可以理解为
while (lines.hasNext()) {
//读取一行
line = lines.next();
//-e pattern1
operate(line, pattern1);
//-e pattern2
operate(line, pattern2);
//...
...
}
回到例子
while (lines.hasNext()) {
line = lines.next();
//-e '3d'
if (line.num == 3) delete;
//-e '3d'
if (line.num == 3) delete;
}
//所以最后删掉的是同一行
-f 读取脚本文件, --file=脚本文件
sed -f ./bin/script config.json
# 可以通过多个-f使用多个脚本文件,并且可以和-e一起使用
sed -f ./bin/script -f ./bin/script1 -e 's/Data/Info/g' config.json
作用:指定后边的值为一个存储着脚本集的文件,会把文件中的脚本读取执行
创建脚本文件,内容如下:
s/test/osp/g
s/Id/User/g
例子:
# 读取sed.ss中的脚本集合并执行
sed -f sed.ss config.json

注意:
1.文件中的脚本可以添加引号包裹,也可不加
2.sed脚本文件中的脚本写在一行需要用分号;隔开

- 使用-f后,后边的文件名时可以用tab键自动填补的
-i , -i[sufix] , --in-place[=suffix]编辑原文件
sed -i ‘s/test/osp/g’ config.json
作用:开启修改原文件
sed命令默认会读取一份文件缓存进行操作,添加了-i才会编辑原文件。
- 当-i 后边跟有后缀suffix时,在修改原文件前,会备份,备份文件后缀就是suffix的值
例子:
# 修改原文件,test -> osp
sed -i 's/test/osp/g' config.json
# 修改原文件前使用后缀进行备份
# osp -> test, 并备份为config.json-bak
sed -i-bak 's/osp/test/g' config.json

注意:
- -i和备份后缀之间没有空格
- 使用-i后,就不会再在命令行输出变更后内容
原理:-i会把原来的sed处理后的输出重定向到一个临时文件中,再改名,实现修改原文件,所以不会有输出。因此要控制好输出的内容,比如使用脚本选项p额外打印时,和使用选项 -n不输出时。-ni“清空文件内容”。 - -i 是接纳参数的,所以想连用选项需要 -ri 或者 -i -r分开,不能使用-ir,会让 r 被当作 -i 的参数。
-r, --regexp-extended 扩展正则
# BRE 不加-r也能用一般的正则BRE(Base Regex Expression)
sed -r 's/[0-9]/1/g' config.json
# ERE 加上-r可以使用扩展正则表达式ERE(Extended Regex Expression)
# 详细参考文章末尾 正则表达式详解
其他不常用
- -c, --copy 使用复制形式操作
sed -i -c 's/test/osp/g' config.json
作用:sed命令使用-i时,是把sed处理结果写入临时文件,再改名mv为原文件。使用-c后,会用cp取代mv。(cp默认覆盖已存在文件)
造成的差异参考cp和mv的区别。
-
–follow-symlinks 直接 -i 修改文件时跟随软链接
-
-l N, --line-length=N
指定“l”命令的换行期望长度 -
–posix
关闭所有 GNU 扩展 -
-s, --separate
将输入文件视为各个独立的文件而不是一个长的连续输入 -
-u, --unbuffered
从输入文件读取最少的数据,更频繁的刷新输出 -
-b 使用二进制模式打开文件,CR+LF 不被特殊处理,兼容 WIN32/CYGWIN/MSDOS/EMX
-
-z, --null-data,用 NUL 字符分隔行
3.4.脚本基本语法
这里的只做参考,不同的操作语法有不同处。
- 注意,之前常用的‘s/原内容/新内容/选项’只是s替换操作需要后边/g选项结尾,否则报错
选项不是常规语法的一部分,只是一些语法需要。 - 这里的选项也先不用管,直接用g
#常用语法 (是标准语法的简化,范围默认操作全文每一行)
'操作 操作内容'
# 操作和操作内容有无空格分隔都可
## 操作s 操作内容 /old/new/g, 每一行都处理有old的话,替换old为new
sed 's/old/new/g' osp.txt
## 操作i 操作内容 monkey, 这样全文每一行前都会插入monkey
sed 'i monkey' osp.txt
# 标准,就是前边也加了行匹配
'/匹配项/操作 操作内容'
# 常用语法实际
# 可以理解为 预选择/操作/操作内容,看几个例子就明白了
## 匹配项http 操作s 操作内容/com/cn/g, 预选择有http的行,把com改为cn,全文修改,s需要/g选项
sed '/http/s/com/cn/g' web
## 匹配项http 操作i 操作内容monkey, 只在匹配http的行插入monkey
sed '/http/i monkey'
# 另一种简化,行号匹配,行号两端都无斜杠
# 适用i,a,c,p,d
'匹配行号操作/操作内容'
## 预选择3到8行,把http替换为https
sed '3,8s/http/https' web
## 预选择3到8行,打印
sed '3,8p' web
## 打印第2行
sed '2p' web
## 打印第2行,第4行
sed '2p;4p' web
## $删除最后一行
sed '$d' web
| 操作标志 | 作用 |
|---|---|
| s | [替换] 原文本替换为新文本 (只有整行删除操作,部分删除操作可借助s替换为空值实现) |
| i | [插入行] 匹配内容对应行前插入内容 |
| a | [追加行] 匹配内容对应行后添加内容 |
| c | [整行替换] 匹配内容对应行整行替换为新文本 当使用行号匹配了连续多行时,比如 ‘2,9c’,2-9行会当作一个整体替换为新文本,见例子。 |
| d | [删除整行] 删除整行 |
| p | [打印] 打印匹配内容所在行 |
| r | 读取文件 |
| w | 写入文件 |
| = | 输出行号 |
| y | 不常用,非单词替换,详情看例子 |
- 样例文件web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=张麻子
- 例子
s替换
# s替换
## id替换为usrId
[monkey@osp~01] /home/monkey> sed 's/id/usrId/g' web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
usrId=1
name=张麻子
i插入行
# i插入行
# i后边的都被视为要插入的内容,所以要把匹配项放在i之前,而且i后边不要加斜杠,可以加空格
## 在1到3行前添加 ip地址
[monkey@osp~01] /home/monkey> sed '1,3i ip地址' web
ip地址
add1=www.monkey.com
ip地址
add2=http://127.0.0.1:80
ip地址
add3=https://monkeys.cn
id=1
name=张麻子
[monkey@osp~01] /home/monkey>
## 包含monkey的行前添加 保姆猴网
[monkey@osp~01] /home/monkey> sed '/monkey/i 保姆猴网' web
保姆猴网
add1=www.monkey.com
add2=http://127.0.0.1:80
保姆猴网
add3=https://monkeys.cn
id=1
name=张麻子
[monkey@osp~01] /home/monkey>
&的值为原内容的值
# 给 张麻子 加上括号(),无需写(张麻子),只需要(&)
[monkey@osp~01] /home/monkey> sed 's/张麻子/(&)/g' web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=(张麻子)
a追加行
# a追加,和i用法一致
## 在包含monkey的行之后添加 网络地址已失效
[monkey@osp~01] /home/monkey> sed '/monkey/a 网络地址已失效' web
add1=www.monkey.com
网络地址已失效
add2=http://127.0.0.1:80
add3=https://monkeys.cn
网络地址已失效
id=1
name=张麻子
[monkey@osp~01] /home/monkey>
d删除行
# d删除,只需匹配行,无需操作内容
## 删除包含add的行
[monkey@osp~01] /home/monkey> sed '/add/d' web
id=1
name=张麻子
## 删除1-4行
[monkey@osp~01] /home/monkey> sed '1,4d' web
name=张麻子
c整行替换
# c整行替换
# c之后都被视为文本,无需加斜杠
## 包含id的行,整行替换为 "未知字段"
[monkey@osp~01] /home/monkey> sed '/id/c"未知字段"' web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
"未知字段"
name=张麻子
## 把1-5行替换为 "已删除"
# !!!注意,会把1-5行整个替换为1个"已删除"
[monkey@osp~01] /home/monkey> sed '1,3c"未知字段"' web
"未知字段"
id=1
name=张麻子
p打印
# 打印,一般配合sed -n 使用,达到只打印变更了的行内容
## 不添加 -n 时,会打印全部内容,符合p匹配条件的行被重复打印
[monkey@osp~01] /home/monkey> sed '1,5p' web
add1=www.monkey.com
add1=www.monkey.com
add2=http://127.0.0.1:80
add2=http://127.0.0.1:80
add3=https://monkeys.cn
add3=https://monkeys.cn
id=1
id=1
name=张麻子
name=张麻子
# 添加 -n 静默模式,执行时不输出
## 打印2-3行
[monkey@osp~01] /home/monkey> sed -n '2,3p' web
add2=http://127.0.0.1:80
add3=https://monkeys.cn
## 打印包含monkey的行
[monkey@osp~01] /home/monkey> sed -n '/monkey/p' web
add1=www.monkey.com
add3=https://monkeys.cn
r读取文件
[monkey@osp~01] /home/monkey> cat append
age=18
extend=空
## 在第三行后追加从append文件中读取的内容
[monkey@osp~01] /home/monkey> sed '3r append' web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
age=18
extend=空
id=1
name=张麻子
[monkey@osp~01] /home/monkey> cat note
地址已失效
## 包含add的行都添加note中内容
[monkey@osp~01] /home/monkey> sed '/add/r append' web
add1=www.monkey.com
地址已失效
add2=http://127.0.0.1:80
地址已失效
add3=https://monkeys.cn
地址已失效
id=1
name=张麻子
w写入文件
# w写入文件, 配合其他命令,用;隔开
# 当web.bak文件已存在,则覆盖
## 使用w把sed的处理结果写入web.bak文件
[monkey@osp~01] /home/monkey> sed 's/add/address/g; w web.bak' web
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
id=1
name=张麻子
## w不是把输出到终端的内容导向到新文件,所以不会受静默模式-n 和 打印匹配的行p 影响
[monkey@osp~01] /home/monkey> sed -n 's/add/address/p; w web.bak' web
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
[monkey@osp~01] /home/monkey> cat web.bak
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
id=1
name=张麻子
# 删除的例子
[monkey@osp~01] /home/monkey> # sed '1,3d; w web.bak' web
id=1
name=张麻子
age=18
extend=空
[monkey@osp~01] /home/monkey> # cat web.bak
id=1
name=张麻子
age=18
extend=空
y非单词替换
# y 非单词匹配,就是不把原值当作一个单词,而是拆分为字母按顺序分别替换
# 所以原值和新值长度必须一致
# 还要注意,和s差不多,但是后边没有g或者p选项,而且必须还有/,否则报错
## 把i都替换为I,把d都替换为s
[monkey@osp~01] /home/monkey> sed 'y/id/Is/' web
ass1=www.monkey.com
ass2=http://127.0.0.1:80
ass3=https://monkeys.cn
Is=1
name=张麻子
# 报错例子
# 多了选项g
[monkey@osp~01] /home/monkey> sed 'y/id/Is/g' web
sed:-e 表达式 1,字符 9:命令后含有多余的字符
# 少了斜杠
[monkey@osp~01] /home/monkey> sed 'y/id/Is' web
sed:-e 表达式 1,字符 7:未终止的“y”命令
# 长度不一致
[monkey@osp~01] /home/monkey> sed 'y/id/Isw/' web
sed:-e 表达式 1,字符 9:“y”命令的字符串长度不同
=输出行号
# = 输出行号,只想获取行号,不获取内容一般配合sed -n
## 输出包含add的行的行号
[monkey@osp~01] /home/monkey> sed '/add/=' web
1
add1=www.monkey.com
2
add2=http://127.0.0.1:80
3
add3=https://monkeys.cn
id=1
name=张麻子
## 配合sed -n
[monkey@osp~01] /home/monkey> sed -n '/add/=' web
1
2
3
分号;
同一个语法脚本中多个操作可以用分号隔开,类似于-e写多个脚本
需要注意 i插入,a追加,c替换整行这些操作会把后边的都当作文本处理,导致;失效,所以包含这种操作的要么只有一个,用分号放在最后,要么用-e
# add替换为address, id行前插入 这是id
[monkey@osp~01] /home/monkey> sed 's/add/address/g; /id/i 这是id' web
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
这是id
id=1
name=张麻子
age=18
extend=空
# i操作后的会被当做文本
[monkey@osp~01] /home/monkey> sed 's/add/address/g; /id/i 这是id; /name/i 这是name' web
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
这是id; /name/i 这是name
id=1
name=张麻子
age=18
extend=空
感叹号!
取反
# 注意感叹号!要放在匹配项和操作之间
# 删除2-4行以外的行
[monkey@osp~01] /home/monkey> sed -e '2,4!d' web
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
# 不包含monkey的行的add替换为ip
[monkey@osp~01] /home/monkey> sed '/monkey/!s/add/ip/g' web
add1=www.monkey.com
ip2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=张麻子
定界符
默认是 / 斜杠
当匹配项内容或操作内容包含/时,可以用 | 做分界符
也可以使用\进行转义
# /d替换为ww, | 为分界符
[monkey@osp~01] /home/monkey> echo abc/ddd | sed 's|/d|ww|g'
abcwwdd
# /d替换为ww \/进行转义
[monkey@osp~01] /home/monkey> echo abc/ddd | sed 's/\/d/ww/g'
abcwwdd
3.5. 选项
选项可以为空,但是最后的 / 必须有,否则报语法错误
# 为空默认是g的效果
# 所有add都替换为address
[monkey@osp~01] /home/monkey> sed '1,3s/add/address/' web
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
# g 全文操作
# 所有add都替换为address
[monkey@osp~01] /home/monkey> sed '1,3s/add/address/g' web
address1=www.monkey.com
address2=http://127.0.0.1:80
address3=https://monkeys.cn
# p 打印,同打印操作,只是作为s的一个选项
[monkey@osp~01] /home/monkey> sed 's/=/:/p' web
add1:www.monkey.com
add1:www.monkey.com
add2:http://127.0.0.1:80
add2:http://127.0.0.1:80
add3:https://monkeys.cn
add3:https://monkeys.cn
id:1
id:1
name:张麻子
name:张麻子
# /数字 /1 /2 /3 等等 ,等用于 /1g /2g /3g
# 作用:一行中有多个匹配项,从第n个匹配项开始替换
# 注意不是从第几行开始,而是同一行第几个匹配内容
## 从第二个匹配项开始替换
[monkey@osp~01] /home/monkey> echo monkeymonkeymonkey | sed 's/monkey/nanny/2g'
monkeynannynanny
3.6.模式空间命令
奶猴级别详解,易消化。
先理解 [模式空间] 和 [暂存空间]
sed 默认处理的就是被逐行读取的模式空间的行,所以不会更改原文件。
这里的伪代码只做参考,不代表其逻辑是这样实现的。
//再来个细致的伪代码
/**
* file 文件
* patternList 执行的多个脚本 -e pattern1 -e pattern2 ...
*/
sed(file, patternList) {
patternSpace.init(); //模式空间
holdSpace.init(); //暂存空间
lines = file.getLines(); //获取行
while (lines.hasNext()) { //有下一行,则继续
line = lines.next();
patternSpace.add(line); //添加一行
operate(patternSpace, patterns);//对模式空间内的行进行处理
patternSpace.printAll();//打印处理后的内容
patternSpace.clear();//清空模式空间
}
}
小猴们请看:完全没有用到暂存空间
sed只需要模式空间就可以完成操作,而暂存空间只是给了方便用户完成一些复杂或者说不可能操作的一个空间。
比如,小猴要把两个瓶子里的水交换,那么就需要另一个瓶子才能做到,暂存空间就是这样用的。
类似于玩数据结构,用list实现一个栈或者队列。
| 命令 | 作用 |
|---|---|
| n | 打印模式空间当前行,读取下一行到模式空间,执行脚本中的操作。 |
| N | 读入下一行,追加到模式空间行后面,此时模式空间有两行,脚本的操作对两行都有效。 |
| h | 把模式空间里的所有行复制到暂存空间。(覆盖暂存空间原来的) |
| H | 把模式空间里的所有行追加到暂存空间。 |
| g | 用暂存空间的所有内容替换模式空间的行。 |
| G | 把暂存空间的所有内容追加到模式空间的行后。 |
| x | 将暂存空间的所有内容和模式空间里的当前行互换。 |
3.6.1 n
# 来看看n,n本身也是一个脚本命令,一般用分号或者-e与其他命令隔开
# sed 'n;d' web 隔行删除
[monkey@osp~01] /home/monkey> sed 'n;d' web
add1=www.monkey.com
add3=https://monkeys.cn
name=张麻子
原理
//sed 'n;d' web
// patternList.get(0) = n;
// patternList.get(1) = d;
n (patternSpace, lines) {
patternSpace.printAll(); //打印所有
patternSpace.clear(); //清空
patternSpace.add(lines.next()); //下一行写入模式空间
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
//这里假设是java面向对象,参数都是对象,所以调用者作为参数的patternSpace,lines的值被改变了。
}
d () {
delete(); //删除行
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
}
/**
* 所以,当第1行进入模式空间开始执行时,遇到n (第1行被直接输出,第2行写入),n执行完毕,执行d,删除第2行
* 第3行按原进度进入模式空间开始执行,遇到n...
* ...
*/
3.6.2 N
这个更难理解了,看结果先
[monkey@osp~01] /home/monkey> sed 'N;d' web
name=张麻子
# 再写入一行,此时里边6行内容
[monkey@osp~01] /home/monkey> echo age=18 >> web
[monkey@osp~01] /home/monkey> cat web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
id=1
name=张麻子
age=18
# 执行结果为空
[monkey@osp~01] /home/monkey> sed 'N;d' web
[monkey@osp~01] /home/monkey>
//sed 'N;d' web
// patternList.get(0) = N;
// patternList.get(1) = d;
N (patternSpace, lines) {
if (!lines.hasNext()) {
return; //如果下一行不存在了,就直接停止执行。
}
patternSpace.add(lines.next()); //下一行写入模式空间
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
//这里假设是java面向对象,参数都是对象,所以调用者作为参数的patternSpace,lines的值被改变了。
}
d () {
delete(); //删除行
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
}
/**
* 第1行进入,执行N (把第2行强行加入),执行d(删除行),删掉2行,打印结果:都是空
* 第3行,执行N(第4行强行加入),执行d(删除行),打印结果,都是空
* 第5行执行N,因为没有第6行,提前return了,后边的d就没执行,打印:第5行。
* 所以偶数行都会被删除,奇数行会有最后一行因为提前中止不被删除。
*/
N,n都学了,看个复杂例子
[monkey@osp~01] /home/monkey> sed 'N;n;s/=/:/g' web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3:https://monkeys.cn
id=1
name=张麻子
age:18
# N 把下一行读入,此时有两行,都是=,还没执行替换处理,下一条命令
# n 打印所有(2行),清空,读入第3行,下一条命令
# s 替换= 为:无下一条命令,打印,清空,读入..
# 所以前三条输出为 = = :
# 读入第4行, N...
# 一次类推,每三个一循环...
3.6.3 h
// 模式空间所有内容复制到暂存空间,覆盖暂存空间内容
h (patternSpace, holdSpace) {
holdSpace.clear(); //清空暂存
holdSpace.addAll(patternSpace.getAll()); //复制所有模式空间内容到暂存
if (patternList.hasNext())
patternList.next().run(); //执行下一个pattern
}
// h只是复制到暂存空间,对模式空间内容不影响,如果后续没有再转回来的操作,则不影响原来的结果。
3.6.4 h,H,g,G,x 都是同理,参考h就能理解。
这里写一个简单例子
[monkey@osp~01] /home/monkey> sed 'h;H;g;G;x;s/=/:/g' web
add1:www.monkey.com
add1:www.monkey.com
add2:http://127.0.0.1:80
add2:http://127.0.0.1:80
add3:https://monkeys.cn
add3:https://monkeys.cn
id:1
id:1
name:张麻子
name:张麻子
age:18
age:18
拿出一张纸,画两个筐…
| 操作 | 模式空间 | 暂存空间 |
|---|---|---|
| >>>>第一行<<<< | add1=www.monkey.com | null |
| h 模式复制到暂存(覆盖) | add1=www.monkey.com | add1=www.monkey.com |
| H 模式追加到暂存 | add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com |
| g 暂存替换到模式 | add1=www.monkey.com add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com |
| G 暂存追加到模式 | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com |
| x 交换模式和暂存 | add1=www.monkey.com add1=www.monkey.com | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
| 替换=为: | add1:www.monkey.com add1:www.monkey.com | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
| 打印全部,清空模式 打印内容为: add1:www.monkey.com add1:www.monkey.com | null | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
| >>>>第二行<<<< | add2:http://127.0.0.1:80 | add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com add1=www.monkey.com |
| h 模式复制到暂存(覆盖) | add2:http://127.0.0.1:80 | add2:http://127.0.0.1:80 |
| … | … | … |
有非常多复杂和有趣的,总之,两个容器,倒来倒去,玩吧。
3.x.常用操作
3.x.1.注释掉某行
# ^正则代表行首,替换为#
# i是插入新行,不能用
[monkey@osp~01] /home/monkey> sed -n '1,5s/^/#/g' web
#add1=www.monkey.com
#add2=http://127.0.0.1:80
#add3=https://monkeys.cn
id=1
name=张麻子
[monkey@osp~01] /home/monkey>
3.x.2.清空内容
# 操作前无匹配项,默认全文,参考4.脚本基本语法
[monkey@osp~01] /home/monkey> sed 'd' web
[monkey@osp~01] /home/monkey>
3.x.3 利用模式空间暂存空间实现文件内容反转
# '1!G;h;$!d'
[monkey@osp~01] /home/monkey> cat web
add1=www.monkey.com
add2=http://127.0.0.1:80
add3=https://monkeys.cn
[monkey@osp~01] /home/monkey> sed '1!G;h;$!d' web
add3=https://monkeys.cn
add2=http://127.0.0.1:80
add1=www.monkey.com
解析一下,拿个纸,画俩筐…
| 操作 | 模式 | 暂存 | 备注 |
|---|---|---|---|
| >>第一行<< | add1… | null | |
| 1!G 操作是G,匹配行设定了1! 非行号为1 即除了第一行,其他行执行G | add1… | null | 行号为1,不执行G |
| h 复制模式到暂存 | add1… | add1… | |
|
!
d
<
b
r
/
>
操作是
d
删除,匹配行设定
!d<br />操作是d删除,匹配行设定
!d<br/>操作是d删除,匹配行设定! 非最后一行 即除了最后一行,都执行删除 | null | add1… | 不是最后一行,执行删除 |
| 打印,清空 | null | add1… | 打印值为空 |
| >>第二行<< | add2… | add1… | |
| 1!G | add2… add1… | add1… | 行号为2,执行G 暂存追加到模式 |
| h | add2… add1… | add2… add1… | |
| $1d | null | add2… add1… | 不是最后一行,执行删除 |
| 打印,清空 | null | add2… add1… | 打印值为空 |
| >>第三行<< | add3… | add2… add1… | |
| 1!G | add3… add2… add1… | add2… add1… | 行号为3,执行G |
| h | add3… add2… add1… | add3… add2… add1… | |
| $!d | add3… add2… add1… | add3… add2… add1… | 最后一行,不执行删除 |
| 打印,清空 | null | add3… add2… add1… | 打印结果: add3… add2… add1… |
4.正则表达式
有需要详解的命令或者想了解的知识或者本文有描述不准确的地方可以评论或者私信



590

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



