目录
sed 流式编辑器
①sed基本原理
>>>sed(stream editor)是一个流式编辑器,是一个程序,,用于对输入流执行文本转换操作
>>>sed只能通过一次输入流,每次的输入流只能被处理一次
>>>将读入到模式空间的行号通过sed的行号计数器记录在内存中
>>>sed数据缓冲空间
sed对输入流进行操作,需要将其加载到缓冲空间后才能操作
sed有两个缓冲空间:模式空间(pattern_space),一直处于活动的空间
保持空间( hold_space ),是一个辅助性的空间
>>>对模式空间中行的处理逻辑:
(1)读取输入流的第一行到模式空间中(此时行号计数器记录的行号为1)(移除读取到的行的尾随换行符)
(2)当行被预设定的模式匹配上时,则使用sed程序内部的命令进行处理,
不管内容是否匹配,都会默认自动输出模式空间中处理过后的内容 (尾部默认加上换行符)
(3)清空模式空间中的内容
(4)然后从输入流读取下一行到模式空间(行号计数器更新为下一行),并循环上面的操作,直到输入流中所有行都被处理完成
>>>通过一个shell伪代码理解sed程序对输入流的处理逻辑:
for ((line=1;line<=1last_line_num;++line)) #外循环即sed循环
do
read $line to pattern_space;
while pattern_space is not null #内循环即SCRIPT循环
do
excute cmd1 in SCRIPT;
excute cmd2 in SCRIPT;
excute cmd3 in SCRIPT;
……
auto_print; #自动输出
remove_pattern_space; #清空模式pattern_space
done
done
②sed程序语法格式
sed OPSIONS SCRIPT INPUT_STREAM
说明:
OPSIONS 即选项
SCRIPT 即命令的集合,由寻址和命令组成
INPUT_STREAM 即输入流
OPTION选项
常用选项 | 描述 |
-V | 输出sed的版本 |
-n | 禁止 auto_print,输出的内容为空 |
-e | 指定多个表达式 例:sed -e SCRIPT1 -e SCRIPT2 INPUT_STREAM |
-f | -f script_file 指定SCRIPT文件(命令集合) |
-i | 对输入流中的内容进行修改,将修改后的内容写入原文件 |
-r | 使用扩展的正则表达式(类似egrep) |
-s | 对于多个文件,每个文件当成独立的输入流,分别处理 |
SCRIPT命令集合——由寻址和命令组成
>>>SCRIPT写法规则 addr1{cmd1;cmd2};addr2{cmd3;cmd4}
addr表示寻址,cmd表示命令 (命令之间和表达式之间用 ";"隔开)
sed寻址 line addressing
>>>为何要寻址
寻址是对模式空间中的内容进行匹配,根据匹配规则决定是否执行对应的命令
若匹配,则执行相应命令
若不匹配,默认情况下直接将模式空间中的内容输出,然后清空模式空间并进入下一个sed循环
>>>寻址即匹配,匹配可能是单行,也可以是一个范围,也可以是用正则表达式去匹配
>>>寻址格式:add1[,add2]:表示从add1至add2这个范围
sed常用匹配方式如下:
匹配方式 | 描述 |
‘N’ | 指定一个行号 |
‘N,M’ | 指定一个范围,从第N行到第M行 |
‘FIRST~STEP’ | 从FIRST开始,每隔STEP后的行会被定位(2~3,定位到2、5、8、11行) |
‘$’ | 匹配最后一个文件的最后一行(使用-s或-i时,匹配每个文件的最后一行) $不能用作数学计算 |
‘/REGEXP/’或 ‘\%REGEXP%' | 将选择能被正则表达式REGEXP匹配到的所有的行 修饰符“i”,“I”、“M”不能用作空正则表达式 |
‘/REGEXP/I’ 或 ‘\%REGEXP%I’ | I表示匹配的时候忽略大小写 不能用i,因为i是个命令表示插入 |
‘/REGEXP/M’或‘\%REGEXP%M’ | 多行匹配(以\n为分隔符) >>>执行N命令进入多行模式,在当前行后面添加“\n”,并读取第二行内容到模式空间 用^[a-z]$对模式空间的内容“a\nb”进行匹配(以\n为分隔符) a符合匹配要求被替换为X(无g修饰符,只匹配一次,所以b不被替换) 输出模式空间中的内容 >>>清空模式空间,读取第三行的内容,行号计数器更新为3,循环上面步骤 |
‘ADDR1,+N’ | 匹配到ADDR1和其后的N行 |
‘ADDR1,~N’ | 匹配到ADDR1和其后的行,直到出现N的倍数行 (ADDR1=4,N=3,定位到4、5、6行) |
‘0,/REGEXP/’ | 使用0作为起始地址,sed程序会尝试对第一行匹配REGEXP; 若第一行匹配到,则搜索范围立即结束; 若以行号1作为起始地址,则会从第二行开始匹配,直到匹配成功 |
寻址之sed正则表达式——用于模式匹配
建议:少用GNU特有的表达式
/正则表达式/ | 描述 | 举例 |
'CHAR' | 单个普通字符与自身匹配 | ‘abc’ |
'.' | 匹配任意单个字符(除换行符) | 如果在点号字符的位置没有字符,那么模式不成立(可以是空格) |
'*' | 紧跟在它前面的单元应匹配零次或多次 | a*b 表示b前面可以没有a或者多个a,可以是b或者aaaab |
'\+' GNU extension | 紧跟在它前面的单元应匹配一次或多次 | a\+b 表示b前面至少有一个a,可以是ab或者aaab |
'\?' GNU extension | 紧跟在它前面的单元应匹配零次或一次 | /^a\?b$/ 表示输出b或者ab |
'.*' | 匹配任何字符串,包括空字符串 | 输出所有行 |
'.\+' | 匹配任何字符串,但包含至少一个字符的字符串 | 只输出非空白行 |
'\{N\}' | 紧跟在它前面的单元应精准匹配N次 |
|
'\{N,M\}' | 紧跟在它前面的单元应匹配至少N次,最后M次 |
|
'\{N,\}' | 紧跟在它前面的单元应匹配至少N次 |
|
'\(REGEXP\)' | 将正则表达式的一部分括起来组成一个单元, | ifconfig | sed -n 's/\([0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}\)/\1(ip)/gp' 将输出的ip后面追加(ip) |
'\DIGIT' | 引用\(REGEXP\) | \1表示引用被匹配到的第一个分组 |
'^' 脱字符 | 匹配行的开始 | sed '/^$/d' test.txt :过滤空白行 |
'$' 美元符 | 匹配到行的结尾 |
|
'[LIST]' | 匹配字符组 | [0-9]表示0~9任意数字都可以匹配 ; [ae]表示匹配a或者e |
'[^LIST]' | 排除字符组 | [^0-9]表示除了数字0~9都可以匹配 |
'REGEXP1\|EXGREP2' GNU extension | 连接两个子表达式,表示或的关系, |
|
'REGEXP1EXGREP2' | 连接两个正则表达式,表示与的关系 |
|
'\n' | 匹配换行符 |
|
'\CHAR' | 对特殊字符进行转义后匹配 |
|
'[-]' | 字符或数字的范围 | [a-zA-Z0-9] |
sed命令
根据作用空间进行划分分为:
>>>作用于模式空间pattern_space的命令(p、d、q/Q、=、y、n、w、r、s、a、i、c、N、D、P)
>>>作用于保持空间hold_space的命令(h、H、g、G、x)
>>>输入一系列命令需要使用使用大花括号{}括起来
sed命令如下
命令 | 描述 |
p | 强制输出命令,即使使用-n选项,也能输出内容 |
d | 作用是删除,用于删除整个模式空间中的内容,即使d命令后面有命令也不会执行 |
q/Q | 退出当前sed程序,不再执行后面的命令和输入流 |
= | 输出匹配到的行的行号(=命令输出的行号是内存空间,不受-n选项影响) |
y | 根据映射进行替换,根据位置关系进行替换 (被替换字符串,和替换字符串长度必须相等)'y/abc/def/' |
n | 作用是输出当前模式空间并清空模式空间,手动读取下一行,行号计数器自动更新到要读取的那一行 sed -n ‘n,p’test.txt :输出偶数行 |
w filename | 将输入流写到指定文件中 |
r filename | 将一个文件插入到数据流中 |
s | 替换匹配的内容 >>>FLAGS:修饰符 需求:将ifconfig && ls || pwd 替换成 ifconfig && pwd || ls sed ‘s#&&\(.*\) ||\(.*\)#\&\&\2 ||\1#g’ |
a、i、c | 格式:【a|i|c】 TEXT 当有输出流时,这三个命令就会半路”劫杀“(命令和TEXT之间有无空格,有无转义符都一样) a命令表示追加到输出流内容中再输出,追加在输出流的尾部 |
N、D、P | N命令:读取下一行内容追加到当前模式空间的尾部,追加时 原有内容与新内容用换行符“\n”隔开 实现将模式空间中的内容一行行删除(只有在多行模式才可以生效) |
h、H g、G、x | h命令:将模式空间中的内容覆盖到保持空间,注意模式空间中的内容仍然保留 |
③sed高级
标签命令
作用:用于控制SCRIPT循环中执行流的循环和分支语句,可以根据标签跳转到某一行命令继续执行
>>>:LABEL
通过这个格式去定义一个标签,不可放在地址表达式后面
>>>b LABEL
无条件跳转到标签LABEL上 ,只有bLABEL会导致死循环
/REGEXP/bx,如果匹配到表达式,就会跳转到LABEL标签处
如果LABEL省略,则跳转到SCRIPT的尾部,跳到进行auto_print前面
>>>t LABEL
如果对最近读入s命令能够执行成功,则跳转到LABEL标签处
>>>T LABEL
如果对最近读入s命令没有执行成功,则跳转到LABEL标签处
滑动窗口
>>>输出文件最后五行(单用模式空间,效率慢,适合小文件)
cat 1.txt | sed -n 'N;N;N;:r;N;$!s/[^\n]*\n//;tr;p' (若p改为P,即输出文件倒数第5行,P命令用于输出模式空间第一个\n之前的内容)
/[^\n]*\n/ :表达式匹配到第一行的内容,然后删除它
第一次执行s命令:1\n2\n3\n4\n5
第二次执行s命令:2\n3\n4\n5\n6
第三次执行s命令:3\n4\n5\n6\n7
>>>输出文件最后五行(用模式空间和保持空间,适合大文件)
cat 1.txt | sed -n 'H;5,${g;s/[^\n]*\n//;h};$p'
执行流程:
第一次执行sed循环: 保持空间内容为:\n1
第二次执行sed循环: 保持空间内容为:\n1\n2
第三次执行sed循环: 保持空间内容为:\n1\n2\n3
第四次执行sed循环: 保持空间内容为:\n1\n2\n3\n4
第五次执行sed循环: 保持空间内容为:1\n2\n3\n4\n5
第六次执行sed循环: 保持空间内容为:2\n3\n4\n5\n6
…….
sed中变量的使用和替换问题
>>>shell中单引号、双引号、无引号时的作用:决定命令行中哪些单词会被shell解析
>>>规范:sed "shell_content"'sed_content' INPUT_STREAM
(1)单引号: 当变量需要被sed解析,而不被shell解析,使用单引号
单引号内所有字符都是普通字符(包括$)
单引号内不能包括单引号字符,即使转义也不行,因为此时转义字符也是一个普通字符
单引号内包括变量不会被解析,不会命令替换和算术运算,不会进行路径拓展
使用sed,$,!,{} 对于这些符号需要用单引号
(2)双引号:对于需要被shell解析的字符,可以使用双引号
双引号内的所有字符都为普通字符('\','$','`'除外)
对于需要被shell解析的字符,可以使用双引号,也可以不加任何引号
(3)无引号:
等同于双引号,但会进行大括号和波浪号扩展
④sed实战
(1)shell变量与sed变量的引号使用
sed -n "`expr $(wc -l <1.txt) - 4`"',$p' 1.txt
>>> `expr $(wc -l <1.txt) - 4`需要被shell解析,不能加单引号
>>> $p的"$"要被sed解析成最后一行,必须使用单引号,避免被shell解析
(2)将连续空行压缩为一行
sed '/./,/^$/!d' 1.txt
(3)删除开头空行
sed '/./,$!d' 1.txt
(4)删除结尾空行
sed ':start;/^\n*$/{$d;N;b start}' 1.txt
>>>/^\n*$/ :匹配到的是空行
(5)删除每行开头的空格和tab前导空白
sed 's/^[\t ]*//' 1.txt
(6)过滤所有HTML标签
sed 's/<[^>]*>//g;/^$/d;s/^[ ]*//' 1.txt