sed 高级命令用例
基本的 sed 命令处理文本替换大家都已经非常熟悉了,对于一些高级应用比较难处理. 这里收集一些经典场景下的文本处理;供大家学习参考.
多行转一行
待处理文档内容如下,文件名 a.txt
;
aa
bb
cc
dd
1. 命令 N
命令添加多行
gsed -E ':a;$!N;s/\n/ /g;ta' a.txt
命令解析,首先把命令拆成多行:
:a ; # 定义一个标签 a,同时执行一个空命令
$!N; # 定义一个命令:如果不是最后一行时添加下一行,否则什么也不做;
s/\n/ /g;# 全局搜索'\n'字符,替换为空格.
ta; # 如果最近的一个 s 命令执行替换成功.则跳转到 a 标签.否则不执跳转
; # 什么也没有.结束,此时不做特殊处理时,会默认打印模式空间的内容.并清空模式空间
2. 使用 '-z'
选项
sed -z 's/\n/ /g' a.txt
参考:
-z
--null-data
--zero-terminated
Treat the input as a set of lines, each terminated by a zero byte (the ASCII ‘NUL’ character) instead of a newline. This option can be used with commands like ‘sort -z’ and ‘find -print0’ to process arbitrary file names.
特定行合并
1. 合并不固定行
如下:一个文件由一个单行字母开头. 下面跟着若干行以
ms
结尾的参数数据.现在要把这些ms
结尾的数据合并到字母行.
输入文件:b.txt
a
112ms
115ms
100ms
b
10ms
12ms
40ms
30ms
10ms
c
200ms
100ms
要求输出:
a 112ms 115ms 100ms
b 10ms 12ms 40ms 30ms 10ms
c 200ms 100ms
命令行:
# gsed = gnu sed, mac 环境
gsed -E -n '/^[a-z]+/{:a;$!N;s/\n(.*ms$)/\t\1/;ta};P;D;' b.txt
命令释义:
/^[a-z]+/{ # 匹配地址空间[a-z]+,即以字母开头的行.作为处理开始.然后执行命令块(以花括号包裹)
:a; # 定义一个标签 a,作为命令地址标号.方便跳转
$!N; # 当前行为非最后一行时,执行命令'N',追加下一行
s/\n(.*ms$)/\t\1/; # 在上面命令可能追加了一行.也可能没有追加一行.
# 1-如果没有追加成功.不包含'\n',s 命令不执行替换.
# 如果追加行不以 'ms'结束.s 命令也不执行替换.
# 反之:如果追加了一行.此行也是以'ms'结尾.则执行替换.取匹配组1:'\1',亦即去除换行符号:
ta; # s 命令进行了正确替换时,跳转到标签:'a',否则命令块执行结束.
}
P; # 打印模式空间中的第一行数据.
D; # 删除模式空间中的第一行数据.如果模式空间中还有数据,直接开始下一个循环.如果没有数据,先读取一行后.再开始下一个循环.
2. 合并多行慢查询日志
打印日志时,为了日志方便人工观察.日志输出时往往会进行格式化.比如如下的日志文件;在进行日志统计的时候需要进行合并行后才能方便统计一个慢查询的出现次数.
输入:slow.log
XXXX 其它无关日志
[2019/03/31 13:02:59.555][ERROR][slow_sql_logger:125] slow sql 826 millis.
SELECT
id,order_id,version
FROM
order_info
WHERE
order_status = 999
AND pay_status = 999
AND create_time < ?
AND id > ?
ORDER BY id ASC
LIMIT ?
["2019-03-31 12:12:58",0,100]
XXXX 其它无关日志
输出:
SELECT id,order_id,version FROM order_info WHERE order_status = 999 AND pay_status = 999 AND create_time < ? AND id > ? ORDER BY id ASC LIMIT ?
命令:
sed -E -n -e '/slow sql [0-9]+ millis\.$/!be;:a;/\]$/!{N;ba}; s/\n/ /g;s/\s+/ /g; s/(.*millis\. )(.*)(\[.*\])/\2/p;:e;' slow.log
命令释义:
'/slow sql [0-9]+ millis\.$/!be;:a;/\]$/!{N;ba}; s/\n/ /g;s/\s+/ /g; s/(.*millis\. )(.*)(\[.*\])/\2/p;:e;'
# 转化为:
/slow sql [0-9]+ millis\.$/!be; # 首次匹配,没有匹配到慢查询起始行:'slow sql xxx milllis.' , 如果没有匹配到,跳转到结束标签:'e'
:a; # 设定一个标签 'a',用于标记循环起始
/\]$/!{N;ba}; # 当前行不为']'结束时,添加下一行.同时跳转到'a',继续添加行.直到当前行以']'结束
s/\n/ /g; # 换行符号 替换为空格:' '
s/\s+/ /g; # 进行空白字符合并
s/(.*millis\. )(.*)(\[.*\])/\2/p; # 提取 sql 部分,掐头去尾,同时打印出模式空间中的内容;
:e; # 无具体命令,结束位置标签
3. 合并以 '\'
折行的多行
在很多场景下,如果一行数据太行.不方便书写时,会进行以
\
进行换行.方便书写与阅读.但在程序执行时,会进行行合并
思路:
当遇到以\
结束的行时,循环读取下一行.直到读取的行不是以\
结尾.最后打印出内容.
输入:
a b c \
d\
e
f
g
h i j\
k l\
n
o
p
q
h\
输出:
a b c d e
f
g
h i j k l n
o
p
q
h
命令:
sed ':a;/\\$/{;s/\\$/ /; $!N; s/\n/ /;ta}' multiline.txt
释义:
:a; # 定义标签:'a'
/\\$/ { # 匹配'\'结尾的行.进行拼接处理.如果不匹配,不进行任何处理.原样输出.
;s/\\$/ /; # 搜索'\'替换为空格(这个替换不一定最好,按语义可能是直接去除更好)
$!N; # 替换完成后,如果不是最后一行,读取下一行(兼容最后一行包含\的情况);
s/\n/ /; # 对上面加入的新行可能存在的'\n'(因为可能读取不成功.不存在新行)进行去除.
ta # 如果去除成功.进行循环,否则结束当前循环.最后使用默认的输出.打印合并好的行
}
sed 多行处理技术
一般场景下sed
都是单行处理.使用最多的场景是单行内容替换,如上所列举的所有示例,都是多行处理技术.
默认情况下,sed 是无法进行如s/\n/ /g
这样的直接替换的.这里简单介绍下多行处理中常用的一些命令;
多行处理命令
D
_deletes_line from the pattern space until the first newline, and restarts the cycle.
释义: 删除模式空间内容,直到遇到第一个换行符号.然后重启循环;
G
_appends_line from the hold space to the pattern space, with a newline before it.
释义: 添加行从保持空间到模式空间.同时在其前面会添加一个换行符
H
_appends_line from the pattern space to the hold space, with a newline before it.
释义: 添加行从模式空间到保持空间.同时在其前面会添加一个换行符
N
_appends_line from the input file to the pattern space.
释义: 添加行从输入文件到模式空间
P
_prints_line from the pattern space until the first newline.
释义: 打印行从模式空间,直到第一个换行符
,也就是打印第一行;
分支与流程控制
参考:https://www.gnu.org/software/sed/manual/sed.html#Branching-and-flow-control