【运维进阶】shell三剑客
shell三剑客
在 Linux 系统中,shell 三剑客指的是 grep
、sed
、awk
这三个文本处理工具。它们各自擅长不同的场景,结合使用可以高效完成复杂的文本分析、过滤、替换等任务,是 shell 脚本和系统管理中不可或缺的工具。
- grep:文本过滤工具(Global Regular Expression Print)
- sed:流编辑器(Stream Editor),擅长修改文本。
- awk:文本分析工具(以创始人名字命名:Aho, Weinberger, Kernighan),擅长格式化输出。
三剑客的典型配合场景:
# 从 access.log 中筛选出状态码为 404 的行,提取 IP(第 1 列),并统计每个 IP 的出现次数
grep ' 404 ' access.log | awk '{print $1}' | sort | uniq -c
grep ' 404 '
:过滤出 404 错误的行;awk '{print $1}'
:提取这些行的 IP 地址(第 1 列);sort | uniq -c
:排序并统计每个 IP 的出现次数。
掌握这三个工具,能极大提升 Linux 文本处理和系统管理的效率。
而其中sed 与 awk 又并称为 Linux/Unix 世界的两大王牌文字处理器:
- sed 侧重点是替换。
- awk 侧重点是分割和重新合成。
sed 使用手册
sed 介绍
sed,英文全称 stream editor ,是一种非交互式的流编辑器,能够实现对文本非交互式的处理,功能很强大。
sed 工作流程
sed 工作流程,说起来很简单。
读取行 -> 执行 -> 显示 -> 读取行 -> 执行 -> 显示 -> .... -> 读取行 -> 执行 -> 显示
-
读取行
sed 从输入流 (文件、管道、标准输入流)中读取 一行 并存储在名叫
pattern space
的内部空间中。sed 是行文字处理器。每次只会读取一行。
sed 内部会有一个计数器,记录着当前已经处理多少行,也就是当前行的行号。
-
执行
按照 sed 命令定义的顺序依次应用于刚刚读取的 一行 数据。
默认情况下,sed 一行一行的处理所有的输入数据。但如果我们指定了行号,则只会处理指定的行。
-
显示
把经过 sed 命令处理的数据发送到输出流(文件、管道、标准输出),并同时清空
pattern space
空间。 -
上面流程一直循环,直到输入流中的数据全部处理完成。
sed 注意事项
- 默认不修改原文件,仅输出处理结果,需加
-i
选项才直接修改(建议先备份)。 - 命令格式为
sed [选项] '指令' 文件
,指令需用单引号包裹,避免 shell 解析。 - 正则表达式默认使用基本正则(BRE),扩展正则需加
-E
选项。 - 替换指令
s/旧内容/新内容/
中,分隔符可自定义(如s#旧#新#
),避免与内容冲突。 - 多行处理需注意模式空间与保持空间的使用,避免意外截断。
sed 命令语法
sed 帮助
[lth@controller ~ 19:46:33]$ sed --help
用法: sed [选项]... {脚本(如果没有其他脚本)} [输入文件]...-n, --quiet, --silent取消自动打印模式空间-e 脚本, --expression=脚本添加“脚本”到程序的运行列表-f 脚本文件, --file=脚本文件添加“脚本文件”到程序的运行列表--follow-symlinks直接修改文件时跟随软链接-i[SUFFIX], --in-place[=SUFFIX]edit files in place (makes backup if SUFFIX supplied)-c, --copyuse copy instead of rename when shuffling files in -i mode-b, --binarydoes nothing; for compatibility with WIN32/CYGWIN/MSDOS/EMX (open files in binary mode (CR+LFs are not treated specially))-l N, --line-length=N指定“l”命令的换行期望长度--posix关闭所有 GNU 扩展-r, --regexp-extended在脚本中使用扩展正则表达式-s, --separate将输入文件视为各个独立的文件而不是一个长的连续输入-u, --unbuffered从输入文件读取最少的数据,更频繁的刷新输出-z, --null-dataseparate lines by NUL characters--helpdisplay this help and exit--versionoutput version information and exit如果没有 -e, --expression, -f 或 --file 选项,那么第一个非选项参数被视为
sed脚本。其他非选项参数被视为输入文件,如果没有输入文件,那么程序将从标准
输入读取数据。
GNU sed home page: <http://www.gnu.org/software/sed/>.
General help using GNU software: <http://www.gnu.org/gethelp/>.
E-mail bug reports to: <bug-sed@gnu.org>.
Be sure to include the word ``sed'' somewhere in the ``Subject:'' field.
-n, --quiet, --silent 取消自动打印模式空间-e 脚本, --expression=脚本 添加“脚本”到程序的运行列表-f 脚本文件, --file=脚本文件 添加“脚本文件”到程序的运行列表--follow-symlinks 直接修改文件时跟随软链接-i[扩展名], --in-place[=扩展名] 直接修改文件(如果指定扩展名就备份文件)-l N, --line-length=N 指定“l”命令的换行期望长度--posix 关闭所有 GNU 扩展-r, --regexp-extended 在脚本中使用扩展正则表达式-s, --separate 将输入文件视为各个独立的文件而不是一个长的连续输入-u, --unspaceed 从输入文件读取最少的数据,更频繁的刷新输出--help 打印帮助并退出--version 输出版本信息并退出-a ∶新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~-c ∶取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!-d ∶删除,因为是删除啊,所以 d 后面通常不接任何咚咚;-i ∶插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);-p ∶列印,亦即将某个选择的资料印出。通常 p 会与参数 sed -n 一起运作~-s ∶取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法
sed 命令语法简写格式如下:
sed [option] [sed-command] [input-file]
总的来说 sed 命令主要由四部分构成:
sed
命令。[option]
命令行选项,用于改变 sed 的工作流程。[sed-command]
是具体的 sed 命令。[input-file]
输入数据,如果不指定,则默认从标准输入中读取。
示例1:模拟cat命令打印文件内容
[lth@controller ~ 21:31:49]$ cat data.txt
I am studing sed
I am www.twle.cn
I am a no-work-men
I am so handsome[lth@controller ~ 21:31:54]$ sed '' data.txt
I am studing sed
I am www.twle.cn
I am a no-work-men
I am so handsome
对照着 sed 命令的语法格式:
- 这里未使用
option
。 ''
,对应着[sed-command]
为具体的 sed 语句。data.txt
,对应着[input-file]
,用于提供输入数据。
示例2:从标准输入中读取数据
如果我们没有提供输入文件,那么 sed 默认会冲标准输入中读取数据。
[lth@controller ~ 21:32:04]$$ sed ''
# 输入hello world,并回车
hello world
# 输出hello world
hello world
# 按ctrl+d推出
常用选项
选项 | 说明 |
---|---|
-n | 安静模式(默认 sed 会输出所有行,加 -n 后只输出匹配或指定操作的结果) |
-e | 多重编辑,允许指定多个编辑命令 |
-f | 从脚本文件读取 sed 命令 |
-i | 直接修改原文件(慎用,会覆盖原文件) |
-e 选项
-e
用于在命令行中直接指定要执行的 sed
命令,命令需用单引号 ''
包裹。
# 打印data.txt文件内容(空命令表示默认打印所有内容)
[lth@controller ~ 21:52:36]$ sed -e '' data.txt# 若只有一个命令,-e选项可以省略
[lth@controller ~ 21:52:39]$ sed '' data.txt
# `-e ''` 表示执行空命令,`sed` 默认会打印文件所有内容,所以这两个命令效果相同,都会输出 `data.txt` 的全部内容。# -e 选项可以多次使用,依次执行多个命令(1d表示删除第1行)
[lth@controller ~ 21:53:34]$ sed -e '1d' -e '2d' -e '5d' data.txt
I am a no-work-men
I am so handsome
# 因为文件中不存在第五行,所以"5d"命令无效果
# 多次使用 `-e` 分别指定了三个删除命令:删除第 1 行(`1d`)、删除第 2 行(`2d`)、删除第 5 行(`5d`)。执行后,`data.txt` 中剩余的内容被打印出来。# 使用分号(;)分隔多个命令,效果与多次使用-e相同
[lth@controller ~ 21:54:47]$ sed -e '1d;2d;5d' data.txt
I am a no-work-men
I am so handsome
-f 选项
sed 还支持把所有 sed 命令保存在一个普通的文本文件里,然后通过 -f
选项来运行这个文件。
当把 sed 存储在文件中时,需要注意 每一个 sed 命令独自成一行。
文件的作用仅仅用于存储命令而已,因此存储 sed 命令的文件并没有任何特殊,可以是一个 .txt
文本文件。
# 创建存储sed命令的文件scripts,包含三个命令(每行一个)
[lth@controller ~ 21:56:51]$ echo -e "1d\n2d\n5d" > scripts# 查看scripts文件内容
[lth@controller ~ 21:56:57]$ cat scripts
1d
2d
5d# 通过-f选项执行scripts文件中的命令
[lth@controller ~ 21:57:16]$ sed -f scripts data.txt
I am a no-work-men
I am so handsome
# `scripts` 文件中依次存放了 `1d`、`2d`、`5d` 三个命令(每行一个),`sed -f scripts data.txt` 会读取并执行这些命令,效果与 `-e` 选项的示例完全相同。
-n 选项
默认情况下,sed
会自动打印处理后的内容;-n
选项会关闭自动打印,只有明确使用 p
命令(打印)时才会输出内容。
# 无任何输出:-n关闭自动打印,且没有指定打印命令
[lth@controller ~ 21:58:00]$ sed -n '' data.txt# 打印第一行记录:1p表示明确打印第1行
[lth@controller ~ 21:58:10]$ sed -n '1p' data.txt
I am studing sed
sed 行寻址
基本语法
sed '[地址] 命令' 文件
- 地址(line address):用来指定
sed
命令作用的行,可以是行号、范围、正则匹配等。 - 命令:如
p
、d
、s
等。
示例文件
[lth@controller ~ 22:00:43]$ echo 'This is 1
This is 2
This is 3
This is 4
This is 5 ' > test
演示
示例1: 打印所有行
# 打印所有行
[lth@controller ~ 22:01:17]$ cat test | sed ''
# 输出结果
This is 1
This is 2
This is 3
This is 4
This is 5 # sed 默认打印模式缓冲区中所有内容。# 等效于
[lth@controller ~ 22:01:21]$ cat test | sed -n 'p'
# -n 关闭sed打印模式缓冲区中所有内容。
# p命令,明确打印输出模式缓冲区中所有内容。
示例2: 打印特定行
# 打印第1行
[laoma@shell ~]$ cat test | sed -n '1p'# 输出结果
This is 1# 打印第最后一行
[laoma@shell ~]$ cat test | sed -n '$p'
# 输出结果
This is 5
示例3: 打印第1行到3行
[lth@controller ~ 22:07:05]$ cat test | sed -n '1,3p' # '1,3p' 打印第1至3行
# 输出结果
This is 1
This is 2
This is 3
示例4: 打印第3行到最后一行
[lth@controller ~ 22:07:13]$ cat test | sed -n '3,$p'
# '3,$p' 打印第3行到最后一行($表示末尾)
# 输出结果
This is 3
This is 4
This is 5
示例5: 连续输出,打印第2行以及后续两行
[lth@controller ~ 22:07:13]$ cat test | sed -n '2,+2p'
# '2,+2p' 打印第2行及之后2行
# 输出结果
This is 2
This is 3
This is 4
示例6: 隔行输出,打印第1行以及后续隔2行输出
[lth@controller ~ 22:07:26]$ cat test | sed -n '1~2p'
# '1~2p' 从第1行开始,每隔2行打印一次
# 输出结果
This is 1
This is 3
This is 5
sed 模式寻址
sed
模式寻址是通过行号或正则表达式定位特定内容,以便对其执行命令(如打印、删除等)。
- 行号寻址:用具体行号、范围(如
1,3
)、特殊符号($
表最后一行)等指定行; - 正则寻址:用
/pattern/
匹配含指定模式的行,或模式范围(如/start/,/end/
)。
基本语法
sed '/模式/ 命令' 文件
/模式/
:正则表达式,用来匹配行命令
:对匹配到的行执行操作,如p
、d
、s
等
示例文件
[lth@controller ~ 22:10:54]$ cat << 'EOF' > ~/test
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
EOF
示例1: 打印含有字符串zhang的行
[lth@controller ~ 22:13:32]$ cat test | sed -n '/zhang/p'
# /zhang/ 匹配包含"zhang"字符串的行,p表示打印,结合-n只输出匹配行
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
示例2: 打印root开头的行到zhang开头的行
[lth@controller ~ 22:13:39]$ cat test | sed -n '/^root/,/^mail/p'
# /^root/ 匹配以"root"开头的行,/^mail/匹配以"mail"开头的行,逗号分隔表示范围,打印这个范围内的所有行
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
示例3: 打印root开头的行到第三行
[lth@controller ~ 22:13:43]$ cat test | sed -n '/^root/,3p'
# /^root/ 匹配以"root"开头的行,3表示第3行,打印从匹配行到第3行的范围
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
示例4: 打印root开头的行到最后一行
[lth@controller ~ 22:13:53]$ cat test | sed -n '/^root/,$p'
# /^root/ 匹配以"root"开头的行,$表示最后一行,打印从匹配行到文件末尾的所有内容
sed 子命令
打印
作用
- p,打印模式空间所有记录。
- P,打印模式空间第一行记录。
语法
格式:[address1[,address2]]p
address1
和address2
分别是 起始地址 和 结束地址,可以是 行号或 模式字符串。address1
和address2
都是可选参数,可以都不填,这时候就是打印所有行,从文件的开头到文件结束。- 如果存在一个,那么就是打印 单行。也就是只打印
address1
指定的那行。 p
命令仅从 模式缓冲区 中打印行,也就是该行不会发送到输出流,原始文件保持不变。
示例:
[lth@controller ~ 22:18:29]$ echo 'This is 1
> This is 2
> This is 3' | sed -n '1{N;p}'
This is 1
This is 2
# 1{...} → 只对第 1 行执行大括号里的命令。
# N → 把 下一行(第 2 行)也读进模式空间,此时模式空间包含:
# This is 1\nThis is 2
# p → 打印整个模式空间。[lth@controller ~ 22:18:34]$ echo 'This is 1
> This is 2
> This is 3' | sed -n '1{N;P}'
This is 1 # N → 把第 1 行和第 2 行一起放到模式空间:This is 1\nThis is 2
# P → 只打印 模式空间的第一行(直到第一个换行符为止)。# 可以记成:
# p = print all
# P = print first
读取下一行
- n,提前读取下一行,覆盖模型空间之前读取的行。模型空间之前读取的行并没有删除,依然打印至标准输出,除非使用-n选项指明不打印。
示例1:打印偶数行内容
[lth@controller ~ 22:20:31]$ echo 'This is 1
> This is 2
> This is 3
> This is 4
> This is 5' | sed -n 'n;p'
This is 2
This is 4# n
# 读入下一行到模式空间,并覆盖当前内容。
# 默认会打印当前模式空间的内容(但因为用了 -n,默认输出被抑制了)。
# p
# 打印模式空间的内容。# 逐步执行:
# 读入第 1 行 "This is 1" → 模式空间
# 执行 n → 丢弃第 1 行,读入第 2 行 "This is 2"
# 执行 p → 打印 "This is 2"
# 读入第 3 行 "This is 3" → 模式空间
# 执行 n → 丢弃第 3 行,读入第 4 行 "This is 4"
# 执行 p → 打印 "This is 4"
# 读入第 5 行 "This is 5" → 模式空间
# 执行 n → 试图读下一行,但没有了 → 结束,不打印# sed -n 'n;p' 的逻辑是:
# 丢掉奇数行,只打印偶数行
- N,简单来说就是追加下一行到模式空间,同时将两行看做一行,但是两行之间依然含有\n换行符。
示例1: 成对合并行
[lth@controller ~ 22:20:49]$ cat test | sed 'N;s/\n/==/'
# cat test:读取test文件内容并输出
# |:管道符,将test文件内容作为sed命令的输入
# sed 'N;s/\n/==/':# N:sed的命令,将下一行内容追加到当前模式空间(即同时处理两行内容)# ;:分隔两个命令# s/\n/==/:替换命令,将模式空间中两行之间的换行符(\n)替换为"=="
# 输出结果中,原本的相邻两行通过"=="连接在一起,如第一行和第二行变成:
root:x:0:0:root:/root:/bin/bash==bin:x:1:1:bin:/bin:/bin/false
# 后续行也是同样规律,第三行和第四行通过"=="连接,以此类推
daemon:x:2:2:daemon:/sbin:/bin/false==mail:x:8:12:mail:/var/spool/mail:/bin/false
说明:
N
,追加下一行到当前行后面,组成一个新行来处理。s/\n/==/
,将新行中\n
换行符替换成==
,末尾的换行符不替换。
示例2: 打印前2行
[lth@controller ~ 22:26:21]$ echo 'This is 1
This is 2
This is 3
This is 4
This is 5' | sed -n '1{N;p}'
This is 1
This is 2
替换
**示例1:**将test文件中的root替换成tankzhang,每行只只替换第一个匹配项,并用grep过滤含替换结果的行
[lth@controller ~ 22:28:45]$ sed 's/root/tankzhang/' test|grep tankzhang
# s/root/tankzhang/:替换命令,每行只替换第一个"root"为"tankzhang"
# 输出显示替换了第一个root的行
tankzhang:x:0:0:root:/root:/bin/bash
**示例2:**将test文件中的root全部替换成tankzhang,g表示全局替换
[lth@controller ~ 22:28:49]$ sed 's/root/tankzhang/g' test |grep tankzhang
# s/root/tankzhang/g:g表示全局替换,每行所有"root"都替换为"tankzhang"
# 输出显示所有root都被替换的行
tankzhang:x:0:0:tankzhang:/tankzhang:/bin/bash
示例3:-n和p结合,只打印发生替换的行(每行只替换第一个匹配)
[lth@controller ~ 22:30:15]$ sed -n 's/root/tankzhang/p' test
# -n关闭自动打印,p表示打印替换后的行,只替换每行第一个"root"
# 仅输出发生替换的行
tankzhang:x:0:0:root:/root:/bin/bash
示例4:-n和gp结合,只打印发生全局替换的行
[lth@controller ~ 22:30:24]$ sed -n 's/root/tankzhang/gp' test
# g全局替换,p打印替换后的行,-n确保只输出这些行
# 仅输出所有root被替换的行
tankzhang:x:0:0:tankzhang:/tankzhang:/bin/bash
**示例5:**在第2-8行中,将以zhang开头的行替换为ying开头,并显示替换行
[lth@controller ~ 22:30:33]$ sed -ne '2,8s/^zhang/ying/gp' test
# 2,8指定行范围,s/^zhang/ying/替换以zhang开头的行为ying开头,gp确保打印替换行
# 输出替换后的行
yingy:x:1000:100:,,,:/home/zhangy:/bin/bash
**示例6:**在以zhang开头的行到含Po的行之间,替换所有zhang为ying并显示
[lth@controller ~ 22:30:49]$ sed -ne '/^zhang/,/Po/ s/zhang/ying/gp' test
# /^zhang/,/Po/指定模式范围,在该范围内将所有zhang替换为ying,gp打印替换行
# 输出范围内替换后的行
yingy:x:1000:100:,,,:/home/yingy:/bin/bash
ba:x:1002:1002::/home/yingy:/bin/bash
@yingying:*:1004:1004::/home/test:/bin/bash
替换中的分隔符可以自定义,默认是/。
示例7: 自定义替换分隔符为 #
。
sed
允许你自定义分隔符,比如 #
、@
、|
等。
[lth@controller ~ 22:30:54]$ sed -n 's#root#hello#gp' test
hello:x:0:0:hello:/hello:/bin/bash
分隔符;和-e选项
需要执行多个sed处理命令时,用分号分开,或者使用 -e
选项。
示例:
- 在第2行到第8行之间,替换以zhang开头的行,用ying来替换
- 在第5行到第10行之间,用goodbay来替换dbus,并显示替换的行
[lth@controller ~ 22:33:00]$ cat test | sed -n '2,8s/^zhang/ying/gp;5,10s#dbus#goodbay#gp'
yingy:x:1000:100:,,,:/home/zhangy:/bin/bash
goodbay:x:81:81:System message bus:/:/bin/false[lth@controller ~ 22:33:06]$ cat test | sed -ne '2,8s/zhang/ying/gp' -ne '5,10s#dbus#goodbay#gp'
yingy:x:1000:100:,,,:/home/yingy:/bin/bash
goodbay:x:81:81:System message bus:/:/bin/false
插入
命令 | 作用 |
---|---|
a | 在匹配行 后 插入 |
i | 在匹配行 前 插入 |
c | 整行替换 |
a 在匹配行下面插入新行
将要插入的东西,插入到匹配行的下面
[lth@controller ~ 22:34:12]$ sed '/root/a====aaaa====' test
root:x:0:0:root:/root:/bin/bash
====aaaa====
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
i 在匹配行上面插入新行
将要插入的东西,插入到匹配行的上面
[lth@controller ~ 22:34:37]$ sed '/root/i====iiii====' test
====iiii====
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
&nobody:$:99:99:nobody:/:/bin/false
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
http:x:33:33::/srv/http:/bin/false
dbus:x:81:81:System message bus:/:/bin/false
hal:x:82:82:HAL daemon:/:/bin/false
mysql:x:89:89::/var/lib/mysql:/bin/false
aaa:x:1001:1001::/home/aaa:/bin/bash
ba:x:1002:1002::/home/zhangy:/bin/bash
test:x:1003:1003::/home/test:/bin/bash
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
删除
作用
- d,删除模式空间所有记录。
- D,删除模式空间第一行记录。
语法
sed '地址范围d' 文件sed '地址范围D' 文件
d 删除 示例
示例1: 删除1,14行
[lth@controller ~ 22:38:06]$ sed -e '1,14d' test
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
示例2: 删除4以后的行,包括第4行,把$当成最大行数就行了。
[lth@controller ~ 22:38:11]$ sed -e '4,$d' test
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
示例3: 删除包括false的行,或者包括bash的行,别忘了加\
[lth@controller ~ 22:38:16]$ sed -e '/\(false\|bash\)/d' test
policykit:x:102:1005:Po
示例4: 删除从匹配root的行,到匹配以test开头的行,中间的行
[lth@controller ~ 22:38:23]$ sed -e '/root/,/^test/d' test
@zhangying:*:1004:1004::/home/test:/bin/bash
policykit:x:102:1005:Po
D 删除 示例
删除当前模式空间开端至\n的内容,放弃之后的命令,对剩余模式空间继续执行sed。
示例1:读取最后一行内容
[lth@controller ~ 22:38:54]$ echo 'This is 1
This is 2
This is 3
This is 4
This is 5' | sed 'N;D'
# 输出内容
This is 5
说明:
- 读取
This is 1
,执行N,得出This is 1
\nThis is 2
\n,执行D。 - 读取
This is 3
,执行N,得出This is 3
\nThis is 4
\n,执行D。 - 读取
This is 5
,执行N,后续无内容,读取失败,放弃后续命令,正常打印This is 5
示例2:删除偶数行
[lth@controller ~ 22:39:07]$ echo 'This is 1
This is 2
This is 3
This is 4
This is 5' | sed 'n;D'# 输出内容
This is 1
This is 3
This is 5
说明:
- 读取
This is 1
,执行n,This is 2
\n覆盖This is 1
\n,执行D删除This is 2
\n,This is 1
\n没有删除,正常打印This is 1
。 - 读取
This is 3
,执行n,This is 4
\n覆盖This is 3
\n,执行D删除This is 4
\n,正常打印This is 3
。 - 读取
This is 5
,执行n,后续无内容,读取失败,放弃后续命令,正常打印This is 5
打印行号
=
打印当前处理的 行号。
语法
sed '=' 文件名
**示例1:**行号与行内容交替显示
[lth@controller ~ 22:40:31]$ sed '=' test
1
root:x:0:0:root:/root:/bin/bash
2
bin:x:1:1:bin:/bin:/bin/false
3
daemon:x:2:2:daemon:/sbin:/bin/false
4
mail:x:8:12:mail:/var/spool/mail:/bin/false
5
ftp:x:14:11:ftp:/home/ftp:/bin/false
6
&nobody:$:99:99:nobody:/:/bin/false
7
zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
8
http:x:33:33::/srv/http:/bin/false
9
dbus:x:81:81:System message bus:/:/bin/false
10
hal:x:82:82:HAL daemon:/:/bin/false
11
mysql:x:89:89::/var/lib/mysql:/bin/false
12
aaa:x:1001:1001::/home/aaa:/bin/bash
13
ba:x:1002:1002::/home/zhangy:/bin/bash
14
test:x:1003:1003::/home/test:/bin/bash
15
@zhangying:*:1004:1004::/home/test:/bin/bash
16
policykit:x:102:1005:Po
**示例2:**行号与行内容并排显示
# 利用 sed 'N;s/\n/:/' 把行号和内容合并到同一行
[lth@controller ~ 22:41:52]$ sed '=' test| sed 'N;s/\n/:/'
1:root:x:0:0:root:/root:/bin/bash
2:bin:x:1:1:bin:/bin:/bin/false
3:daemon:x:2:2:daemon:/sbin:/bin/false
4:mail:x:8:12:mail:/var/spool/mail:/bin/false
5:ftp:x:14:11:ftp:/home/ftp:/bin/false
6:&nobody:$:99:99:nobody:/:/bin/false
7:zhangy:x:1000:100:,,,:/home/zhangy:/bin/bash
8:http:x:33:33::/srv/http:/bin/false
9:dbus:x:81:81:System message bus:/:/bin/false
10:hal:x:82:82:HAL daemon:/:/bin/false
11:mysql:x:89:89::/var/lib/mysql:/bin/false
12:aaa:x:1001:1001::/home/aaa:/bin/bash
13:ba:x:1002:1002::/home/zhangy:/bin/bash
14:test:x:1003:1003::/home/test:/bin/bash
15:@zhangying:*:1004:1004::/home/test:/bin/bash
16:policykit:x:102:1005:Po
写入
w 写入
将模式空间中记录写入到文件中。
示例1: 将root开头的行,写入test3中
# 从test文件中匹配以root开头的行,并将这些行写入test3文件
[lth@controller ~ 22:43:19]$ sed -n '/^root/w test3' test
# -n:关闭自动打印,只处理指定操作
# /^root/:正则表达式,匹配以"root"开头的行(^表示行首)
# w test3:将匹配到的行写入名为test3的文件(w是write的缩写)
# test:作为处理对象的源文件# 查看test3文件内容,确认写入结果
[lth@controller ~ 22:43:24]$ cat test3
root:x:0:0:root:/root:/bin/bash # 这是test文件中以root开头的行,已被写入test3
W 写入
将模式空间中第一条记录写入到文件中。
示例: 写入记录
# 小写w写入
[lth@controller ~ 22:43:28]$ vim scripts
1{
N
w write.log
}
[lth@controller ~ 22:43:38]$ sed -n -f scripts test# 小写w写入包含模式中所有行
[lth@controller ~ 22:43:43$ cat write.log
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false# 大写W写入只包含模式中第一行
[lth@controller ~ 22:43:49]$ vim scripts
1{
N
W write.log
}
[lth@controller ~ 22:43:51]$ sed -n -f scripts test
[lth@controller ~ 22:43:59]$ cat write.log
root:x:0:0:root:/root:/bin/bash
更改
整行替换。
示例:root开头行替换出hello
[lth@controller ~ 22:45:35]$ sed '/^root/chello' test
hello
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
......# 等效下面命令
[lth@controller ~ 22:45:40]$ sed 's/^root.*/hello/' test
多命令执行 {} 和-f 选项
对匹配的内容执行多个命令。
示例1: 打印前两行
[lth@controller ~ 22:46:06]$ echo 'This is 1
This is 2
This is 3' | sed -n '1{N;p}'# 输出结果
This is 1
This is 2
示例2: 替换并打印
[lth@controller ~ 22:46:32]$ sed -n '1,5{s/^root/hello/;p}' test
hello:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
daemon:x:2:2:daemon:/sbin:/bin/false
mail:x:8:12:mail:/var/spool/mail:/bin/false
ftp:x:14:11:ftp:/home/ftp:/bin/false
示例3: 多个命令写成脚本
# 小写w写入
[lth@controller ~ 22:46:41]$ vim scripts
1{
N
w write.log
}
[lth@controller ~ 22:46:53]$ sed -n -f scripts test# 小写w写入包含模式中所有行
[lth@controller ~ 22:46:58]$ cat write.log
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/bin/false
awk 使用手册
awk 介绍
awk 是一种强大的文本处理工具,主要用于对文本数据进行分析、提取和转换。
它按行处理文本,支持模式匹配和动作定义(格式通常为 pattern {action}
),可根据指定条件筛选行并执行处理操作(如打印特定字段、计算统计值等)。
awk 擅长处理结构化文本(如表格数据、日志),默认以空格或制表符分隔字段,通过 $1
$2
等表示第 1、第 2 个字段,$0
表示整行。
常见用途包括数据提取、格式转换、统计分析等,是 shell 脚本中处理文本的重要工具。
awk 命令
基本语法
awk '模式 {动作}' 文件
- 模式(pattern):用来匹配行,可以是正则表达式,也可以是逻辑判断。
- 动作(action):对匹配行执行的操作,用
{}
包裹。
如果省略模式,则作用于所有行。 如果省略动作,默认打印匹配行。
awk 示例
示例文件
[lth@controller ~ 22:51:10]$ cat << 'EOF' > employee.txt
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
EOF
**示例1:**打印雇员信息(默认打印所有行)
[lth@controller ~ 22:51:21]$ awk '{ print }' employee.txt
# { print } 表示对每一行执行打印操作,默认打印整行($0)
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
**示例2:**通过awk脚本文件打印雇员信息
[lth@controller ~ 22:51:30]$ cat commands.awk
{ print } # 脚本中定义打印操作,与示例1逻辑相同
[lth@controller ~ 22:51:32$ awk -f commands.awk employee.txt
# -f 指定脚本文件commands.awk,awk读取并执行其中的命令
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
**示例3:**输出包含"张三"的行
[lth@controller ~ 22:51:41]$ awk '/张三/ { print }' employee.txt
# /张三/ 是匹配模式,只对包含"张三"的行执行{ print }操作
1) 张三 技术部 23
# 等同于(省略print时,默认打印匹配行)
[lth@controller ~ 22:51:46]$ awk '/张三/' employee.txt
1) 张三 技术部 23
$0
,代表整行记录。
示例4: 统计满足特定条件的记录数。
AWK 中的所有变量都不需要初始化,并且会自动初始化为
0
。
[lth@controller ~ 22:52:06]$ awk '
/术/ { count=count+1 } # 匹配含"术"的行,计数器count加1
END { print "Count="count }' employee.txt # END块在处理完所有行后执行,打印统计结果
运行以上代码,输出结果如下:
Count=2 # employee.txt中有2行包含"术"(技术部的两行)
示例5: 输出总长度大于 10 的行。
AWK 提供了一个内建的函数 **length(arg)∗∗用于返回字符串‘arg)** 用于返回字符串 `arg)∗∗用于返回字符串‘arg` 的总长度。
如果要获取某行的总长度,可以使用下面的语法:
length($0)
。同样的,如果要获取某列/字段的总长度,可以使用语法:
length($n)
。如果要判断某行的字符是否大于/小于/等于 N ,可以使用下面的语法:
length($0) > N
。
[lth@controller ~ 22:52:20]$ awk 'length($0)>10 { print $0 }' employee.txt
# length($0)获取整行长度,筛选出长度>10的行并打印
# 因为所有的行的总长度都大于18,因此输出结果如下:
1) 张三 技术部 23
2) 李四 人力部 22
3) 王五 行政部 23
4) 赵六 技术部 24
5) 朱七 客服部 23
grep 使用手册
grep介绍
- 核心功能:grep 是一款强大的文本搜索工具,用于在文件或输入流中查找匹配指定模式(正则表达式或普通字符串)的行。
- 基本用法:默认输出包含匹配模式的整行内容,语法通常为
grep [选项] 模式 [文件]
。 - 模式类型:支持普通字符串精确匹配,也支持正则表达式(如
^
匹配行首、$
匹配行尾、*
匹配任意个字符等)。 - 常用选项:
-i
:忽略大小写匹配;-v
:反向匹配,输出不包含模式的行;-n
:显示匹配行的行号;-r
:递归搜索目录下的所有文件。
- 典型用途:日志分析、配置文件筛选、管道中配合其他命令(如
cat
ls
等)进行内容过滤。
grep 命令语法
- 过滤管道:
command | grep [OPTION]... PATTERNS
- 过滤文件:
grep [OPTION]... PATTERNS [FILE]...
grep
命令帮助信息如下:
[lth@controller ~ 22:59:21]$ grep --help
Usage: grep [OPTION]... PATTERNS [FILE]...
Search for PATTERNS in each FILE.
Example: grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.Pattern selection and interpretation:-E, --extended-regexp PATTERNS are extended regular expressions-F, --fixed-strings PATTERNS are strings-G, --basic-regexp PATTERNS are basic regular expressions-P, --perl-regexp PATTERNS are Perl regular expressions-e, --regexp=PATTERNS use PATTERNS for matching-f, --file=FILE take PATTERNS from FILE-i, --ignore-case ignore case distinctions in patterns and data--no-ignore-case do not ignore case distinctions (default)-w, --word-regexp match only whole words-x, --line-regexp match only whole lines-z, --null-data a data line ends in 0 byte, not newlineMiscellaneous:-s, --no-messages suppress error messages-v, --invert-match select non-matching lines-V, --version display version information and exit--help display this help text and exitOutput control:-m, --max-count=NUM stop after NUM selected lines-b, --byte-offset print the byte offset with output lines-n, --line-number print line number with output lines--line-buffered flush output on every line-H, --with-filename print file name with output lines-h, --no-filename suppress the file name prefix on output--label=LABEL use LABEL as the standard input file name prefix-o, --only-matching show only nonempty parts of lines that match-q, --quiet, --silent suppress all normal output--binary-files=TYPE assume that binary files are TYPE;TYPE is 'binary', 'text', or 'without-match'-a, --text equivalent to --binary-files=text-I equivalent to --binary-files=without-match-d, --directories=ACTION how to handle directories;ACTION is 'read', 'recurse', or 'skip'-D, --devices=ACTION how to handle devices, FIFOs and sockets;ACTION is 'read' or 'skip'-r, --recursive like --directories=recurse-R, --dereference-recursivelikewise, but follow all symlinks--include=GLOB search only files that match GLOB (a file pattern)--exclude=GLOB skip files that match GLOB--exclude-from=FILE skip files that match any file pattern from FILE--exclude-dir=GLOB skip directories that match GLOB-L, --files-without-match print only names of FILEs with no selected lines-l, --files-with-matches print only names of FILEs with selected lines-c, --count print only a count of selected lines per FILE-T, --initial-tab make tabs line up (if needed)-Z, --null print 0 byte after FILE nameContext control:-B, --before-context=NUM print NUM lines of leading context-A, --after-context=NUM print NUM lines of trailing context-C, --context=NUM print NUM lines of output context-NUM same as --context=NUM--group-separator=SEP use SEP as a group separator--no-group-separator use empty string as a group separator--color[=WHEN],--colour[=WHEN] use markers to highlight the matching strings;WHEN is 'always', 'never', or 'auto'-U, --binary do not strip CR characters at EOL (MSDOS/Windows)When FILE is '-', read standard input. With no FILE, read '.' if
recursive, '-' otherwise. With fewer than two FILEs, assume -h.
Exit status is 0 if any line is selected, 1 otherwise;
if any error occurs and -q is not given, the exit status is 2.Report bugs to: bug-grep@gnu.org
GNU grep home page: <http://www.gnu.org/software/grep/>
General help using GNU software: <https://www.gnu.org/gethelp/>
grep 命令选项
模式选择和解释选项
-E 选项
支持扩展正则表达式,相当于 egrep
命令。
# 从words文件中查找包含连续3个"dog"的行
[lth@controller ~ 23:03:54]$ cat words | grep -E '(dog){3}'
# cat words:读取words文件内容并输出
# |:管道符,将words内容作为grep的输入
# grep -E:-E选项启用扩展正则表达式支持
# '(dog){3}':正则表达式,(dog)表示匹配"dog"字符串,{3}表示前面的分组重复3次,即匹配连续3个"dog"("dogdogdog")# 或者使用egrep(等同于grep -E)
[lth@controller ~ 23:04:18]$ cat words | egrep '(dog){3}'
# egrep是grep的扩展版本,默认支持扩展正则表达式,与grep -E效果相同# 输出结果:
dogdogdog # 包含连续3个"dog"的行
dogdogdogdog # 包含连续4个"dog"(前3个满足匹配条件)的行
-e 选项
使用多个 -e
选项匹配多个PATTERNS
。
[lth@controller ~ 23:04:26]$ cat words | grep -e 'cat' -e 'dog'
# 或者
[lth@controller ~ 23:04:28]$ cat words | egrep 'cat|dog'
cat
category
acat
concatenate
dog
dogdog
dogdogdog
dogdogdogdog
hello cat
-f 选项
从文件读取多个 PATTERNS
。
[lth@controller ~ 23:05:37]$ echo -e 'cat\ndog' > pattens_file
[lth@controller ~ 23:05:41]$ cat pattens_file
cat
dog
[lth@controller ~ 23:05:45]$ cat words | grep -f pattens_file
cat
category
acat
concatenate
dog
dogdog
dogdogdog
dogdogdogdog
hello cat
-i 选项
忽略大小写匹配。
[lth@controller ~ 23:08:09]$ cat words | grep -i 'cBt'
# cat words:读取words文件内容
# |:管道符,将内容传递给grep处理
# grep -i 'cBt':-i选项忽略大小写,查找包含与'cBt'大小写无关的匹配内容的行
cbt # 输出结果,'cbt'与'cBt'忽略大小写后匹配
-w 选项
匹配整个单词。
[lth@controller ~ 23:08:11]$ cat words | grep -w 'cat'
# grep -w 'cat':-w选项限定匹配整个单词,'cat'必须作为独立单词出现
# 或者使用正则表达式\b表示单词边界,效果与-w相同
[lth@controller ~ 23:08:19]$ cat words | grep '\bcat\b'
cat # 单独的'cat'单词被匹配
hello cat # 'cat'作为独立单词出现在句中被匹配
-x 选项
匹配整行。
[lth@controller ~ 23:08:36]$ cat words | grep -x 'cat'
# grep -x 'cat':-x选项限定整行匹配,只有内容完全是'cat'的行才会被选中
# 或者使用正则表达式^表示行首、$表示行尾,限定整行内容,效果与-x相同
[lth@controller ~ 23:08:39]$ cat words | grep '^cat$'
cat # 只有内容完全为'cat'的行被输出
输出控制选项
-v 选项
反向匹配,显示与PATTERNS
不匹配的项目。
[lth@controller ~ 23:13:53]$ cat words | egrep -v '^d|^c'
# 排除以d或c开头的行
acat
hello cat# 不看注释行(以#开头,允许前有空格)和空白行
[lth@controller ~ 23:13:59]$ egrep -v '^ *#|^$' /etc/profile
-m 选项
控制最大匹配数目,匹配特定次数后停止匹配。
[lth@controller ~ 23:14:10]$ cat words | grep 'dog'
# 原输出所有含dog的行
dog
dogdog
dogdogdog
dogdogdogdog[lth@controller ~ 23:14:14]$ cat words | grep -m2 'dog'
# -m2:只匹配前2行
dog
dogdog
-c 选项
显示匹配到项目的数量。
[lth@controller ~ 23:14:18]$ cat words | grep -c 'dog'
4 # 共4行含dog
-b 选项
显示匹配项目的字节偏移量。
[lth@controller ~ 23:14:23]$ head -5 words
# 显示words前5行内容
cat
category
acat
concatenate
dog[lth@controller ~ 23:14:27]$ cat words | grep -b 'cat'
# 输出每行匹配到cat的起始字节位置
0:cat
4:category
13:acat
18:concatenate
109:hello cat
-n 选项
显示匹配项目的行号。
[lth@controller ~ 23:14:34]$ cat words | grep -n 'cat'
# 行号:内容
1:cat
2:category
3:acat
4:concatenate
19:hello cat
-o 选项
只显示匹配到的内容,行中其他内容不显示。
[lth@controller ~ 23:14:39]$ cat words | egrep '(dog){3}'
# 原输出含连续3个dog的整行
dogdogdog
dogdogdogdog[lth@controller ~ 23:14:46]$ cat words | egrep -o '(dog){3}'
# 只输出匹配到的连续3个dog部分
dogdogdog
dogdogdog
-q 选项
不显示任何正常输出。一般用于脚本判定文件中是否包含特定内容。
通过特殊变量 $?
查看是否匹配到内容。
# 找到的情况
[lth@controller ~ 23:14:52]$ cat words | egrep -q '(dog){3}'
[laoma@shell ~]$ echo $?
0 # 找到匹配# 找不到的情况
[lth@controller ~ 23:15:23]$ cat words | egrep -q '(dog){3}asdfasfdasf'
[lth@controller ~ 23:15:30]$ echo $?
1 # 未找到匹配
-s 选项
不显示任何错误输出。
[lth@controller ~ 23:15:33]$ grep '^SELINUX=' /etc/shadow /etc/selinux/config
# 原输出包含权限错误
grep: /etc/shadow: Permission denied
/etc/selinux/config:SELINUX=disabled[lth@controller ~ 23:15:41]$$ grep -s '^SELINUX=' /etc/shadow /etc/selinux/config
# -s隐藏错误,只显示正常结果
/etc/selinux/config:SELINUX=disabled
总结
- 对于文本搜索与筛选,
grep
是快速定位的利器,凭借灵活的模式匹配能力,能精准抓取所需内容,是处理文本的第一道高效筛选门。 - 面对文本编辑与批量修改,
sed
以流编辑的特性见长,通过简洁命令实现替换、删除等操作,轻松完成批量文本改造,是文本加工的得力工具。 - 针对结构化数据的分析与处理,
awk
展现出强大的字段处理和统计能力,能按需求提取、计算、转换数据,是深度解析文本的核心帮手。 - 三者相辅相成,通过管道组合可形成 “搜索 - 编辑 - 分析” 的完整工作流,熟练运用能大幅提升文本处理效率,是 shell 环境下不可或缺的实用工具组合。