由于
sed
命令本身特别复杂,完全把此命令搞明白并记住不忘是非常困难的.因此能够对于一些经常使用的场景总结出最佳实践一个不错的方式.
sed 工作模式
其命令格式如下:
sed -e [option] 'command' file
command 的格式构成
[addr]X[options]
一个命令由地址空间,命令字母,以及相应的命令选项(可选),比如命令:4,5p;q43
;首先匹配地址空间 4 行与 5 行,然后执行打印命令 p
,命令 p
没有带可选参数,打印完成后执行 q
命令,命令 q
带了可选项参数:42
表明命令的退出 code 为:42
;下面对此命令格式进行详细的介绍;
[addr]
一个地址空间是后面跟的命令的执行匹配行的一个选择条件.后面的命令只对此地址空间匹配的行产生作用;如下命令:
sed '144s/hello/world/' input.txt > output.txt
Addresses determine on which line(s) the sed
command will be executed. The following command replaces the word ‘hello’ with ‘world’ only on line 144:
如果没写地址空间选项,后面的命令将会在所有的行上进行操作,下面的命令将替换所有行中的单词:hello
为 world
(注:只会替换一行中出现的第一个 hello
单词):
sed 's/hello/world/' input.txt > output.txt
除了使用行号,地址空间也可以包含一个正则表达式来匹配满足此表达式的行;下面的命令将会替换包含单词:apple
的行的 hello
为 world
:
sed '/apple/s/hello/world/' input.txt > output.txt
一个地址范围通过两个地址指定,地址之间用 ,
分隔;地址可以是数字,正则表达式,或者是两者的混合;下面的命令仅仅在 4 到 17 行,替换单词 hello
为 world
;
sed '4,17s/hello/world/' input.txt > output.txt
可以添加符号:!
来取地址空间的补集:取反
(在命令字母前面添加);下面的命令替换单词 hello
为 world
仅仅在那些不包含单词:apple
的行中:
sed '/apple/!s/hello/world/' input.txt > output.txt
The following command replaces the word ‘hello’ with ‘world’ only in lines 1 to 3 and 18 till the last line of the input file (i.e. excluding lines 4 to 17):
sed '4,17!s/hello/world/' input.txt > output.txt
X
X
是一个单字母命令
其常用的命令有:
命令 | 说明 | 示例 |
---|---|---|
# | 注释 | '4,5p # 打印输出4到5行' |
p | 打印匹配行 | '4,5p' |
s | 对匹配行进行替换操作 | s/a/b/g |
= | 显示文件行号 | sed -ne '=;p' aaa.txt |
d | 删除定位行:Delete the pattern space; immediately start next cycle. | sed -e '4,5d' aaa.txt |
q | 第一个模式匹配完成后退出或立即退出 | |
n | If auto-print is not disabled, print the pattern space, then, regardless, replace the pattern space with the next line of input. If there is no more input then sed exits without processing any more commands. |
This command is useful to skip lines (e.g. process every Nth line). |
{cmds} | 一个命令组可以用{}包起来,这个会特别有用.当你想一组命令被一个单地址,或者是地址范围触发时 |
[options]
options 选项针对不同的具体命令有具体的使用方法,也有可能不带选项;
一个脚本命令行或者是一个脚本文件里的命令可以使用分号进行分隔(;
),或者是使用新行进行分隔('\n',ascii 码值为 10);多个脚本可以被同时用参数 -e
或者是 -f
指定;
注:
如上面的命令组
{}
的意义与区别在于,每一个命令是以[地址]X[选项]进行书写,因此这就是一个完整的命令.下一个命令与上一个命令之间由上面的知识可知是通过分号;
或者是新行\n
进行分隔;因此多个命令看起来是这样的3,5p;6,7a//this is a comment;
这里有两个命令,如果这两个命令的地址范围都是3,5
,那可以合并为一个命令组:
seq 10 | sed -n -e '3,5{a//this is a comment;
p;
}'
打印命令 p
打印单行
echo "$var" | sed -n -e '5p' #打印第五行
echo "$var" | sed -n -e '5p;5q;' #打印第五行高效模式
echo "$var" | sed -n -e '$p' #打印最后一行
打印多行
echo "$var" | sed -n -e '5,8p' #打印第5到8行
echo "$var" | sed -n -e '5,$p' #打印第5到最后一行
替换命令: s
s
命令(即替换命令)可能是sed
最重要的命令,它包含非常多的选项,此命令的语法为:'s/regexp/replacement/flags'
其基本概念特别简单:s
命令尝试使用命令中的正则表达式
去匹配模式空间
中的字符串;如果匹配成功,模式空间中的匹配项将会被replacement
替代
标准命令如下, origin 是一个正则表达多,replacement 是替换后的目标字符串
# 标准命令
sed -e 's/regexp/replacement/flags' filename.txt
# 简化命令
sed 's/origin/replacement/' filename.txt
aaa
替换成 bbb
(第一匹配组)
echo "aaa ccc bbb ddd eee" | sed -e 's/aaa/bbb/'
aaa
替换成 bbb
(指定匹配组)
# 替换第二个 'aaa' 为 'bbb'
echo "aaa aaa bbb ddd eee" | sed -e 's/aaa/bbb/2'
aaa
替换成 bbb
(全部替换)
# 替换所有 aaa 为 bbb
echo "aaa aaa bbb ddd eee" | sed -e 's/aaa/bbb/g'
s
命令中的分隔符号 /
The
/
characters may be uniformly replaced by any other single character within any givens
command. The/
character (or whatever other character is used in its stead) can appear in the regexpor replacement only if it is preceded by a\
character.
在替换命令中(即 s
命令)的 /
是可以用任意字符来分隔的:
# 最常见的形式
echo "aaa bb cc" | sed -e 's/a/b/g'
# 这里用#分隔
echo "aaa bb cc" | sed -e 's#a#b#g'
# 用 空格分隔
echo "aaa bb cc" | sed -e 's a b g'
目标串中的 &
在字符串替换的时候,&
代表匹配的字符串的值
场景: 给电话号码添加双引号
echo "phoneNumber:13800138000" | sed -E 's/[0-9]{11}/"&"/g'
phoneNumber:"13800138000"
目标串中使用匹配组
The replacement can contain
\n
(n being a number from 1 to 9, inclusive) references, which refer to the portion of the match which is contained between the nth\(
and its matching\)
. Also, the replacement can contain unescaped&
characters which reference the whole matched portion of the pattern space.
场景: phone:13800138000
修改为:{"phone": "13800138000"}
# 修改需要的部分
echo "something*&&&^^^phone:13800138000sfjdskfsfe" | sed -E 's/(phone):([0-9]{11})/{"\1":"\2"}/g'
# 只输出需要的部分
echo "something*&&&^^^phone:13800138000sfjdskfsfe" | sed -E 's/.*(phone):([0-9]{11}).*/{"\1":"\2"}/g'
去除文件中的 '\r'
echo -e "aaa\r\nbbb\r\nccc" > aaa.txt
sed -e $'s/\r//g' aaa.txt | od -c
行合并
This section uses
N
,D
andP
commands to process multiple lines, and theb
andt
commands for branching
合并特定行
$ cat lines.txt
hello
hel
lo
hello
$ sed '2{N;s/\n//;}' lines.txt
hello
hello
hello
命令解释:
2{N;s/\n//;}
匹配上第二行后,执行命令块:{N;s/\n//;}
,命令块先再读取下一行(命令N
),执行s
命令;搜索\n
替换为空,这样 2-3 行合并在一行.其它行没有匹配上.直接原样输出;
合并 '\'
换行的多行
$ cat 1.txt
this \
is \
a \
long \
line
and another \
line
$ sed -e ':x /\\$/ { N; s/\\\n//g ; bx }' 1.txt
this is a long line
and another line
#TODO: The above requires gnu sed.
# non-gnu seds need newlines after ':' and 'b'
命令解释:
:x /\\$/ { N; s/\\\n//g ; bx }
- 首先定义一个标号
x
; - 匹配以
\
结尾的行,由于\
是特殊字符.因此需要转义\\
,同时只匹配以\
结尾的行,因此\
后面加了一个$
; - 搜索到行后,执行命令块:
{ N; s/\\\n//g ; bx }
; - 以上命令块是一个循环,首先读取下一行.同上,替换
\n
为空;然后无条件跳转b
到标签x
;
5.此时再匹配当前行是否以\
结尾;注意此时行已经发生变化,读取了下一行;
文本多行转一行,并且以 ,
分隔
### 无法实现
echo -e "aaa\nbbb\nccc" > aaa.txt
sed -e 's/\n/,/g' aaa.txt
以上代码无法执行的原因是可以参考:sed 列转行无法返回是因为其处理机制的问题
说明:sed 默认只按行处理,N 可以让其读入下一行,再对\n 进行替换,这样就可以将两行并做一行。但是怎么将所有行并作一行呢?可以采用 sed 的跳转功能。:a 在代码开始处设置一个标记 a,在代码执行到结尾处时利用跳转命令 t a 重新跳转到标号 a 处,重新执行代码,这样就可以递归的将所有行合并成一行。
sed ':a ; $!N; s/\n/,/ ; t a ; ' aaa.txt
命令解释:
命令片断 | 说明 | 作用 |
---|---|---|
:a |
定义一个标签 | 用于控制后面的循环 |
$!N |
$ 表示最后一行,$! 表示非最后一行,N 表示向 模式空间中追加下一行 |
非尾行时追加一行数据到 模式空间 |
s/\n/,/ ; |
执行 s 替换命令,把拼接的两行中 \n 替换为 , |
完成两行合并,去除换行 |
t a ; |
如果最后一次输入的最后一个 s/// 子命令执行成功,跳转到标签 a ,否则跳转到结束 |
控制循环,直到读取到最后一行 |
b label ,无条件跳转到标签 label,如果 label 没有指定,跳转到命令的结尾
t label ,如果最后一次输入的最后一个 s/// 子命令执行成功,跳转到标签 label,如果 label 没有指定,跳转到命令的结尾
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于