sed流编辑器
sed是一种流编辑器,流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。
sed编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在一个命令文本文件中。
sed 的工作流程主要包括读取、执行和显示三个过程
•读取:sed 从输入流(文件、管道、标准输入)中读取一行内容并存储到临时的缓冲区中(又称模式空间,pattern space)。
•执行:默认情况下,所有的sed 命令都在模式空间中顺序地执行,除非指定了行的地址,否则sed 命令 将会在所有的行上依次执行。
•显示:发送修改后的内容到输出流。在发送数据后,模式空间将会被清空。在所有的文件内容都被处理完成之前,上述过程将重复执行,直至所有内容被处理完。在所有的文件内容都被处理完成之前,上述过程将重复执行,直至所有内容被处理完。
注意:默认情况下所有的sed命令都是在模式空间内执行的,因此输入的文件并不会发生任何变化,除非是用重定向存储输出。
sed 处理大文件很卡怎么办(重中之重)
- 用cat 输出 | 交给sed (这时就不通过IO一行行读文件,而是cat一次性读取完成 再缓存中直接取)
cat 文件 | grep / sed / awk
- split切块文件 for循环依次读取执行
目录
避免路径 / 被误判为分隔符 / 使用 \ 或是 直接修改分隔符
sed命令格式
sed -e 'n{ 操作1 操作2 ... }' 文件1 文件2 ...
常用选项
-e 或--expression=:表示用指定命令来处理输入的文本文件,只有一个操作命令时可省略,一般在执行多个操作命令使用 -f 或--file=:表示用指定的脚本文件来处理输入的文本文件。 -h 或--help:显示帮助。 -n、--quiet 或 silent:禁止sed编辑器输出,但可以与p命令一起使用完成输出。 -i:直接修改目标文本文件。
sed -e '操作' 文件1 文件2 ... -e 指定操作字符 sed -n -e '操作' 文件1 文件2 ... -n 不显示 sed -f 脚本文件 文件1 文件2 ... -f 调用文本文件中的命令 sed -i -e '操作' 文件1 文件2 ... -i 会修改文件内容
常用操作
s:替换,替换指定字符。 d:删除,删除选定的行。 a:增加,在当前行下面增加一行指定内容。 i:插入,在选定行上面插入一行指定内容。 c:替换,将选定行替换为指定内容。 y:字符转换,转换前后的字符长度必须相同,对应替换,类似tr命令替换用法。 p:打印,如果同时指定行,表示打印指定行;如果不指定行,则表示打印所有内容;如果有非打印字符,则以 ASCII 码输出。其通常与“-n”选项一起使用,避免输出两次。 n:移动到下一行 =:打印行号。 l(小写L):打印数据流中的文本和不可打印的ASCII字符(比如结束符$、制表符\t) q:跟上数字(例如5q) 指定第几行输出后退出流编辑器
sed命令示例
打印内容
sed -n -e 'p' testfile1 #-n不显示结果 -e指定操作字符 'p'显示结果 cat festfile | sed -n -e 'p' #先cat输出再sed 同义 cat festfile | sed -n 'p' #只有一个操作符的情况可以省略-e
输出行号
sed -n -e '=' testfile1 # '='输出行号 cat festfile | sed -n '=' | tail -l #输出最后一行行号(统计行数) cat festfile | wc -l #更简单的统计行数
打印不可见字符
sed -n -e 'l' testfile1 #'l'打印不可见字符
同时使用多个操作符
sed -n -e '=;p' testfile1 #同时打印行号和内容的3种写法 ; 分隔操作符 sed -n -e '=' -e 'p' testfile1 #同时打印行号和内容的3种写法 每个操作符一个-e sed -n ' #同时打印行号和内容的3种写法 回车分批输入 > = > p > ' testfile1 sed -n '= > p > ' testfile1
使用地址
sed编辑器有2种寻址方式:
1、以数字形式表示行区间
2、用文本模式来过滤出行打印指定行
sed -n '4p' testfile1 #打印第四行 sed -n '$p' testfile1 #打印末尾行 sed -n '1,3p' testfile1 #打印1-3行 !需要加-n屏蔽原来的输出不然默认从第一行输出到最后一行! sed -n '3,$p' testfile1 #打印3行到最后行 sed -n '3p;$p' testfile1 #打印3行和最后行 sed -n '1,+3p' testfile1 #打印1之后的连续3行,即1-4行
退出sed
sed '5q' testfile1 #打印前5行信息后退出,q表示退出
显示奇数行
sed -n 'p;n' testfile1 #打印奇数行 第一行p显示行。第二行n跳过一行到第三行。第三行显示,第四行不显示... sed -n 'n;p' testfile1 #打印偶数行 sed -n '2,${n;p}' testfile1 #第二行开始,执行n;p,输出一行跳过一行(3 5 7 9 11...) sed -n '3 {p;n;n;p}' testfile1 #✨在第三行执行 输出 换行两次 输出(3 5) sed -n '3,${p;n;n;p}' testfile1 #✨在第三行到最后一行执行 输出 换行两次 输出(3 5 6 8 9...) 传统的for循环写法 a=0 for i in $(cat ./testfile1) do let a++ if [ $[a%2] -eq 1 ];then echo "$i 奇数行" else echo "$i 偶数行" fi done
使用文本输出匹配的行
sed -n '/root/p' /etc/passwd #显示包含root的行 /文字/p(显示) sed -n '/^root/p' /etc/passwd #添加正则表达式 root开始的行 sed -n '/bash$/p' /etc/passwd #添加正则表达式 bash结尾的行
多条件根据文本筛选
sed -n -e '/ftp/p' -e '/root/p' /etc/passwd #筛选既有root又有ftp的行 sed -nr '/ftp|root/p' /etc/passwd #合并写法 加上-r 使用扩展正则表达式 sed -n '/ftp\|root/p' /etc/passwd #合并写法 加上 \
行号 字符串 配合使用
sed -n '2,/nobody/p' /etc/passwd #从第二行开始,到第一个包含nobody的行结束 sed -n '2,/nobody/=' /etc/passwd #显示‘从第二行开始,到第一个包含nobody的行结束’的行号 sed -nr '/ro?t/p' /etc/passwd #-r表示支持扩展正则表达式 ?匹配一次前面的内容(o) sed -nr '/ro+t/p' /etc/passwd #-r表示支持扩展正则表达式 +匹配一次或多次前面的内容(o) sed -nr '/ro*t/p' /etc/passwd #-r表示支持扩展正则表达式 +匹配0次或多次前面的内容(o) sed -nr '/ro{1,}t/p' /etc/passwd #-r表示支持扩展正则表达式 {1,}匹配一次或多次前面的内容(o) sed -n '/ro\{1,\}t/p' /etc/passwd #若不加-r代表扩展正则表达式 需要加上 \
删除行
指定行号
sed 'd' testfile1 #全删(因为流编辑器一行行执行,代表每行都执行删除) sed '3d' testfile1 #删除3行 sed '2,4d' testfile1 #删除2-4行
正则表达式
sed '$d' testfile1 #删除最后一行 sed '/^$/d' testfile1 #删除空行 ^$(开头结尾 表示空行) sed '/nologin$/d' /etc/passwd #删除nologin结尾行 sed '/nologin$/!d' /etc/passwd #“!”表示取反操作
删除空行的三种方法
grep -v "^$" tr -s '\n' sed '/^$/ d'
行号 字符串 配合使用
sed '2,/3/d' testfile2 #从第二行开始删到第一个包含3的行 sed '/1/,/3/d' testfile2 #从第一个包含2的行开启删除功能 删到第一个包含3的行 关闭删除功能
✨注意,使用字符串匹配删除,会寻找第一个包含字符串(1)位置的行 开启删除功能 删除到第二个包含字符串(3)位置的行 关闭删除功能。
若接下来的匹配中又遇到了包含第一个字符串(1)位置的行,还会继续开启删除功能,直到遇到第二个包含字符串(3)位置的行 关闭删除功能。(若没有则一删到底)根据字符串匹配删除不够准确 容易误删 最好还是指定行号进行删除
替换
行范围 s/旧字符串/新字符串/替换标记 4种替换标记 数字:替换行内第x个被匹配到的字符串 g:替换所有匹配的字符串 p:打印被替换的行,与-n一起使用 w 文件:将替换的结果写到文件中
sed -n 's/root/admin/p' /etc/passwd #将行内第一个root替换成admin p输出替换的行 -n不显示所有行 sed -n 's/root/admin/2p' /etc/passwd #将行内第二个匹配到的root替换成admin sed -n 's/root/admin/gp' /etc/passwd #行内所有root替换成admin sed -n 's/root//gp' /etc/passwd #删除行内所有root(替换为空)
正则表达式 替换
sed -n 's/^#//p' /etc/passwd #去除注释,开头#替换为空 -n不输出文件 p输出更改结果 sed '1,20 s/^/#/' /etc/passwd #1-20行 行首[空]替换为[#] (添加注释#) sed '/^root/ s/$/#/' /etc/passwd #root开头行 末尾加上# sed '/root/ s/^/#/' /etc/passwd #包含root行 末尾加上# sed '/swap/ s/^/#/' /etc/fatab #注释掉etc/fatab中自动挂载的swap分区 开头加上# sed -rn 's/.*root.*/#&/p' /etc/passwd #直接写正则用s替换,不用传统写法,比较麻烦这种写法 # /.*root.*/ 包含root的行 (.* .代表匹配任意一个字符 *代表匹配前面的表达式0次或多次) # #&中 &表示前面正则表达式匹配到的内容 #在匹配到内容前加上#号
sed文件中读取命令执行
sed -f script.sed /etc/passwd #从script.sed文件中读取sed命令执行 script.sed文件内容 /^root/ s/^/#/ #注释 root开头行 开头加上# s/ftp/mysed/g #ftp 全部 替换为mysed /nologin$/ d #删除nologin结尾行
保存修改结果
方法1 重定向输出 (保存所有) sed '1,20 s/^/#/' /etc/passwd > new.txt #1-20行 添加注释# 并且重定向输出 方法1 重定向输出 (只保存修改的行内容) sed -n '1,20 s/^/#/p' /etc/passwd > new.txt #-n不输出全部 p输出修改内容 方法2 w命令(只保存修改的行内容) sed '1,20 s/^/#/w new.txt' /etc/passwd #1-20行 添加注释# 并且保存 sed '1,20w out.txt' /etc/passwd #1-20行保存
避免路径 / 被误判为分隔符 / 使用 \ 或是 直接修改分隔符
sed -n 's/\/bin\/bash/\/sbin\/nologin/p' /etc/passwd #/bin/bash改为/sbin/nologin 需要加\避免系统误判/路径为/分隔符 #'s/ \/bin\/bash / \/sbin\/nologin /p' 上面的写法可读性差,可以直接替换分隔符 只需要任意字符紧跟替换符号 s 后,就会将这个字符识别为分隔符 sed -n 's!/bin/bash!/sbin/nologin!p' /etc/passwd #使用“!”作为字符串分隔符 sed -i 's9\945\9\99\98\939g' test.txt #将94599替换为9893 并i写入文件 \9代表普通字符 9代表分隔符 # 's9 \945\9\9 9 \98\93 9g' [ \9为用 \ 取消9的分隔符含义,表示普通9字符 ]
插入
替换行
sed '1c hello world' testfile1 #第一行整行替换为 hello world sed '10c hello world' testfile1 #第十行整行替换为 hello world sed '$c hello world' testfile1 #最后行整行替换为 hello world sed '/45/c ABC' testfile2 #包含45的行替换为ABC
转换字符
sed 'y/our/ABC/' testfile2 #使所有的o字符转换成A,所有的u字符转换成B,所有的r字符转换成C
行前行后 插入行
行前插入 #根据行号 sed '1a 123' testfile1 #在第一行后插入123行(第二行:123) sed '$a 123' testfile1 #在最后一行后插入123行(追加效果) sed '1,3a 123' testfile1 #1-3行后都插入123 #根据字符串匹配 sed '/^t/a 123' testfile1 #匹配t开头的行 在行后插入123 行后插入 #根据行号 sed '1i 123' testfile2 #在第一行前插入123行(第一行:123) sed '$i 123' testfile2 #在最后一行前插入123行 sed '1,3i 123' testfile2 #1-3行前都插入123 #根据字符串匹配 sed '/^t/i 123' testfile1 #匹配t开头的行 在行前插入123
读取其他文件内容插入当前文件
sed '5r /etc/resolv.conf' testfile2 #在第5行后追加插入/etc/resolv.conf文件内容 sed '$r /etc/resolv.conf' testfile2 #在最后行后追加插入/etc/resolv.conf文件内容
保持空间 实现复制剪切
sed除了缓冲空间用于逐行处理,还有一个保持空间,可以用于保持存储信息。
剪切效果
sed '/root/{H;d};$G' /etc/passwd #将包含root的行剪切到末尾 H复制到剪切板d删除(结合实现剪切) #G表示粘贴到指定行后 $G粘贴到最后一行 sed '1,3{H;d};$G' /etc/passwd #将1、2行剪切 粘贴到最后一行下面 粘贴到最后一行后可能遇到文件末尾换行符,加上sed '/^$/d' 删除空行 sed '1,3{H;d};$G' /etc/passwd | sed '/^$/d' sed '1,3{H;d};10,$G' /etc/passwd #剪切1-3行 10-最后行每行粘贴
在vi编辑器中在:命令模式中输入✨ 1,3 m 10 把1-3行剪切到10行后
复制效果
sed '1,2H;3,4G' /etc/passwd #将1、2行复制 粘贴到3和4行的下面(3 4行每行后都会加上1 2行内容) sed '1,3H;$G' /etc/passwd #将1、2行复制 粘贴到最后一行下面 复制到最后一行后可能遇到文件末尾换行符,加上sed '/^$/d' 删除空行 sed '1,3H;$G' /etc/passwd | sed '/^$/d' sed '1,3H;10,$G' /etc/passwd #复制1-3行 10-最后行每行粘贴
在vi编辑器中在:命令模式中输入✨ 1,3 co 10 把1-3行复制到10行后
分片重组
先( )指定分片 再\指定每个分片位置重组 echo "111222333" | sed -r 's/(111)(222)/\2\1/' #-r使用扩展正则表达式 #将字符 111 和 222 互换位置 echo "111222333" | sed -r 's/(111)(222)(333)/\3\2\1/' #-r使用扩展正则表达式 #将字符 111 和 222 互换位置 首尾字符交换 echo "111222333" | sed -r 's/^(.)(.*)(.)$/\3\2\1/' #第一个字符 最后一个字符 交换 # 's/^(.)(.*)(.)$ ^(.)第一个任意字符 (.*)任意字符任意次 (.)$最后一个任意字符