当前位置: 首页 > news >正文

【运维进阶】shell三剑客

shell三剑客

在 Linux 系统中,shell 三剑客指的是 grepsedawk 这三个文本处理工具。它们各自擅长不同的场景,结合使用可以高效完成复杂的文本分析、过滤、替换等任务,是 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 工作流程,说起来很简单。

读取行 -> 执行 -> 显示 -> 读取行 -> 执行 -> 显示 -> .... -> 读取行 -> 执行 -> 显示

在这里插入图片描述

  1. 读取行

    sed 从输入流 (文件、管道、标准输入流)中读取 一行 并存储在名叫 pattern space 的内部空间中。

    sed 是行文字处理器。每次只会读取一行。

    sed 内部会有一个计数器,记录着当前已经处理多少行,也就是当前行的行号。

  2. 执行

    按照 sed 命令定义的顺序依次应用于刚刚读取的 一行 数据。

    默认情况下,sed 一行一行的处理所有的输入数据。但如果我们指定了行号,则只会处理指定的行。

  3. 显示

    把经过 sed 命令处理的数据发送到输出流(文件、管道、标准输出),并同时清空 pattern space 空间。

  4. 上面流程一直循环,直到输入流中的数据全部处理完成。

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 命令主要由四部分构成:

  1. sed 命令。
  2. [option] 命令行选项,用于改变 sed 的工作流程。
  3. [sed-command] 是具体的 sed 命令。
  4. [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 命令作用的行,可以是行号、范围、正则匹配等。
  • 命令:如 pds 等。
示例文件
[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 '/模式/ 命令' 文件
  • /模式/:正则表达式,用来匹配行
  • 命令:对匹配到的行执行操作,如 pds
示例文件
[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

  • address1address2 分别是 起始地址结束地址,可以是 行号模式字符串
  • address1address2 都是可选参数,可以都不填,这时候就是打印所有行,从文件的开头到文件结束。
  • 如果存在一个,那么就是打印 单行。也就是只打印 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

说明:

  1. N,追加下一行到当前行后面,组成一个新行来处理。
  2. 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 命令语法

  1. 过滤管道:command | grep [OPTION]... PATTERNS
  2. 过滤文件: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

总结

  1. 对于文本搜索与筛选,grep 是快速定位的利器,凭借灵活的模式匹配能力,能精准抓取所需内容,是处理文本的第一道高效筛选门。
  2. 面对文本编辑与批量修改,sed 以流编辑的特性见长,通过简洁命令实现替换、删除等操作,轻松完成批量文本改造,是文本加工的得力工具。
  3. 针对结构化数据的分析与处理,awk 展现出强大的字段处理和统计能力,能按需求提取、计算、转换数据,是深度解析文本的核心帮手。
  4. 三者相辅相成,通过管道组合可形成 “搜索 - 编辑 - 分析” 的完整工作流,熟练运用能大幅提升文本处理效率,是 shell 环境下不可或缺的实用工具组合。
http://www.xdnf.cn/news/1335367.html

相关文章:

  • RK-Android11-PackageInstaller安装器自动安装功能实现
  • 福昕PDF编辑软件高级版下载与详细图文安装教程!!
  • 力扣 30 天 JavaScript 挑战 第36天 第8题笔记 深入了解reduce,this
  • 【嵌入式电机控制#34】FOC:意法电控驱动层源码解析——HALL传感器中断(不在两大中断内,但重要)
  • day075-MySQL数据库服务安装部署与基础服务管理命令
  • 《算法导论》第 35 章-近似算法
  • imx6ull-驱动开发篇31——Linux异步通知
  • 极其简单二叉树遍历JAVA版本
  • 虚拟机部署HDFS集群
  • Redisson最新版本(3.50.0左右)启动时提示Netty的某些类找不到
  • VR交通安全学习机-VR交通普法体验馆方案
  • 从防抖节流到链表树:编程世界中的抽象优化艺术
  • C++智能指针详解:告别内存泄漏,拥抱安全高效
  • Flask高效数据库操作指南
  • C++ MFC/BCG编程:文件对话框(CFileDialog、CFolderPickerDialog)
  • CFBench评测
  • (一)关于步进电机的FOC控制
  • DeepSeek大模型如何重塑AI Agent?从技术突破到行业落地
  • 意象框架:连接感知与认知的统一信息结构分析——基于上古汉语同源词意义系统的词源学与认知语言学探索
  • (认识异常)
  • SED项目复现学习实录
  • JSON::Value 功能详解:从三目运算符到高级用法
  • Git Commit 提交信息标准格式
  • 48 C++ STL模板库17-容器9-关联容器-映射(map)多重映射(multimap)
  • C++进阶-----C++11
  • 【数据结构】线性表——顺序表
  • Linux Shell 常用操作与脚本示例详解
  • CAMEL-Task1-CAMEL环境配置及你的第一个Agent
  • rsync + inotify 数据实时同步
  • 吴恩达 Machine Learning(Class 3)