Linux Shell 文本处理进阶之sed

01. sed 进阶

01. 多行命令

所有的sed编辑器命令都是针对单行数据执行操作的。在sed编辑器读取数据流时,它会基于换行符的位置将数据分成行。sed编辑器根据定义好的脚本命令一次处理一行数据,然后移到下一行重复这个过程。

有时需要对跨多行的数据执行特定操作。如果要查找或替换一个短语,就更是如此了。

sed编辑器包含了三个可用来处理多行文本的特殊命令。

  • N:将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
  • D:删除多行组中的一行。
  • P:打印多行组中的一行。
02. 单行的next命令

通常sed编辑器在移动到数据流中的下一文本行之前,会在当前行上执行完所有定义好的命令。但单行next命令改变了这个流程。n命令会告诉sed编辑器移动到数据流中的下一文本行,而不用重新回到命令的最开始再执行一遍。

目标是删除首行之后的空白行,而留下最后一行之前的空白行。

cat data1.txt
This is the header line.

This is a data line.

This is the last line

脚本要查找含有单词header的那一行。找到之后, n命令会让sed编辑器移动到文本的下一行,也就是那个空行。

sed '/header/{n ; d}' data1.txt
This is the header line.
This is a data line.
This is the last line.
03. 合并文本行

单行next命令会将数据流中的下一文本行移动到sed编辑器的工作空间(称为模式空间)。多行版本的next命令(用大写N)会将下一文本行添加到模式空间中已有的文本后。

这样的作用是将数据流中的两个文本行合并到同一个模式空间中。文本行仍然用换行符分隔,但sed编辑器现在会将两行文本当成一行来处理。

cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.

sed '/first/{ N ; s/\n/ / }' data2.txt
This is the header line.
This is the first data line. This is the second data line.
This is the last line.

sed编辑器脚本查找含有单词first的那行文本。找到该行后,它会用N命令将下一行合并到那行,然后用替换命令s将换行符(linux的换行符是$,即\r;windows的换行符是ctrl+v,ctrl+m,即^M 或 \r\n)替换成空格。结果是,文本文件中的两行在sed编辑器的输出中成了一行。

如果短语分散在两行中的话,通配符模式(.)来匹配空格和换行符两种情况。

sed 'N ; s/System.Administrator/Desktop User/' data3.txt

但当它匹配了换行符时,它就从字符串中删掉了换行符,导致两行合并成一行。这可能不是你想要的。可以在sed编辑器脚本中用两个替换命令:一个用来匹配短语出现在多行中的情况,一个用来匹配短语出现在单行中的情况。

sed 'N
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data3.tx

这个脚本总是在执行sed编辑器命令前将下一行文本读入到模式空间。当它到了最后一行文本时,就没有下一行可读了,所以N命令会叫sed编辑器停止。如果要匹配的文本正好在数据流的最后一行上,命令就不会发现要匹配的数据(将单行命令放到N命令前面,并将多行命令放到N命令后面)。

sed '
> s/System Administrator/Desktop User/
> N
> s/System\nAdministrator/Desktop\nUser/
> ' data4.txt
04. 多行删除命令

删除命令会在不同的行中查找单词System和Administrator,然后在模式空间中将两行都删掉。

sed 'N ; /System\nAdministrator/d' data4.txt

sed编辑器提供了多行删除命令D,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符。

sed 'N ; /System\nAdministrator/D' data4.txt

如果需要删掉目标数据字符串所在行的前一文本行,它能派得上用场。

cat data5.txt

This is the header line.
This is a data line.

This is the last line.

sed '/^$/{N ; /header/D}' data5.txt
This is the header line.
This is a data line.
This is the last line.
05. 多行打印命令

多行打印命令(P)沿用了同样的方法。它只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符。

sed -n 'N ; /System\nAdministrator/P' data3.txt
On Tuesday, the Linux System
06. 保持空间

模式空间(pattern space)是一块活跃的缓冲区,在sed编辑器执行命令时它会保存待检查的文本。但它并不是sed编辑器保存文本的唯一空间。

sed编辑器有另一块称作保持空间(hold space)的缓冲区域。在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。

命 令 	描 述
h 		将模式空间复制到保持空间
H 		将模式空间附加到保持空间
g 		将保持空间复制到模式空间
G 		将保持空间附加到模式空间
x 		交换模式空间和保持空间的内容

这些命令用来将文本从模式空间复制到保持空间。这可以清空模式空间来加载其他要处理的字符串。

通常,在使用h或H命令将字符串移动到保持空间后,最终还要用g、 G或x命令将保存的字符串移回模式空间。

cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.

sed -n '/first/ {h ; p ; n ; p ; g ; p }' data2.txt
This is the first data line.
This is the second data line.
This is the first data line.

sed -n '/first/ {h ; n ; p ; g ; p }' data2.txt
This is the second data line.
This is the first data line.
07. 排除命令

感叹号命令(!)用来排除(negate)命令,也就是让原本会起作用的命令不起作用。

sed -n '/header/!p' data2.txt
This is the first data line.
This is the second data line.
This is the last line.
sed '$!N;
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data4.txt
On Tuesday, the Linux Desktop
User's group meeting will be held.
All Desktop Users should attend.

美元符表示数据流中的最后一行文本,所以当sed编辑器到了最后一行时,它没有执行N命令,但它对所有其他行都执
行了这个命令。

使用这种方法,你可以反转数据流中文本行的顺序。要实现这个效果(先显示最后一行,最后显示第一行),你得利用保持空间做一些特别的铺垫工作。

在这里插入图片描述

cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.

sed -n '{1!G ; h ; $p }' data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.

它提供了一种在脚本输出中控制行顺序的简单办法。tac命令也会倒序显示一个文本文件。

08. 改变流

通常, sed编辑器会从脚本的顶部开始,一直执行到脚本的结尾(D命令是个例外,它会强制sed编辑器返回到脚本的顶部,而不读取新的行)。

分支
sed编辑器提供了一种方法,可以基于地址、地址模式或地址区间排除一整块命令。这允许你只对数据流中的特定行执行一组命令。

分支(branch)命令b的格式如下:

[address]b [label]

address参数决定了哪些行的数据会触发分支命令。 label参数定义了要跳转到的位置。如果没有加label参数,跳转命令会跳转到脚本的结尾。

cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.

sed '{2,3b ; s/This is/Is this/ ; s/line./test?/}' data2.txt
Is this the header test?
This is the first data line.
This is the second data line.
Is this the last test?

分支命令在数据流中的第2行和第3行处跳过了两个替换命令。要是不想直接跳到脚本的结尾,可以为分支命令定义一个要跳转到的标签。标签以冒号开始,最多可以是7个字符长度。使用标签允许你跳过地址匹配处的命令,但仍然执行
脚本中的其他命令。

sed '{/first/b jump1 ; s/This is the/No jump on/
> :jump1
> s/This is the/Jump here on/}' data2.txt
No jump on header line
Jump here on first data line
No jump on second data line
No jump on last line

也可以跳转到脚本中靠前面的标签上,这样就达到了循环的效果。

echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p
> b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
^C

这个脚本有个问题:它永远不会结束。要防止这个问题,可以为分支命令指定一个地址模式来查找。如果没有模式,跳转就应该结束。

echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p
> /,/b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.

现在分支命令只会在行中有逗号的情况下跳转。在最后一个逗号被删除后,分支命令不会再执行,脚本也就能正常停止了。

测试

测试(test)命令(t)也可以用来改变sed编辑器脚本的执行流程。如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果替换命令未能匹配指定的模式,测试命令就不会跳转。

测试命令使用与分支命令相同的格式。

[address]t [label]

跟分支命令一样,在没有指定标签的情况下,如果测试成功,sed会跳转到脚本的结尾。

测试命令提供了对数据流中的文本执行基本的if-then语句的一个低成本办法。举个例子,如果已经做了一个替换,不需要再做另一个替换,那么测试命令能帮上忙。

sed '{
> s/first/matched/
> t
> s/This is the/No match on/
> }' data2.txt
No match on header line
This is the matched data line
No match on second data line
No match on last line
echo "This, is, a, test, to, remove, commas. " | sed -n '{
> :start
> s/,//1p
> t start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
09. 模式替代

如果你在模式中用通配符(.)来匹配多个单词呢?

echo "The cat sleeps in his hat." | sed 's/.at/".at"/g'
The ".at" sleeps in his ".at".

遗憾的是,用于替代的字符串无法匹配已匹配单词中的通配符字符。

&符号
&符号可以用来代表替换命令中的匹配的模式。不管模式匹
配的是什么样的文本,你都可以在替代模式中使用&符号来使用这段文本。这样就可以操作模式所匹配到的任何单词了。

echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
The "cat" sleeps in his "hat".

当模式匹配了单词cat, "cat"就会出现在了替换后的单词里。当它匹配了单词hat, "hat"就出现在了替换后的单词中。

替代单独的单词
&符号会提取匹配替换命令中指定模式的整个字符串。有时你只想提取这个字符串的一部分。

sed编辑器用圆括号来定义替换模式中的子模式。你可以在替代模式中使用特殊字符来引用每个子模式。替代字符由反斜线和数字组成。数字表明子模式的位置。 sed编辑器会给第一个子模式分配字符\1,给第二个子模式分配字符\2,依此类推。

当在替换命令中使用圆括号时,必须用转义字符将它们标示为分组字符而不是普通的圆括号。这跟转义其他特殊字符正好相反。

echo "The System Administrator manual" | sed '
> s/\(System\) Administrator/\1 User/'
The System User manual
echo "That furry cat is pretty" | sed 's/furry \(.at\)/\1/'
That cat is pretty
echo "1234567" | sed '{
> :start
> s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
> t start
> }'
1,234,567
10. 在脚本中使用 sed

使用包装脚本

cat reverse.sh
#!/bin/bash
# Shell wrapper for sed editor script.
# to reverse text file lines.
#
sed -n '{ 1!G ; h ; $p }' $1
#
./reverse.sh data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.

重定向 sed 的输出
默认情况下, sed编辑器会将脚本的结果输出到STDOUT上。你可以在shell脚本中使用各种标准方法对sed编辑器的输出进行重定向。可以在脚本中用$()将sed编辑器命令的输出重定向到一个变量中,以备后用。

cat fact.sh
#!/bin/bash
# Add commas to number in factorial answer
#
factorial=1
counter=1
number=$1
#
while [ $counter -le $number ]
do
factorial=$[ $factorial * $counter ]
counter=$[ $counter + 1 ]
done
#
result=$(echo $factorial | sed '{
:start
s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
t start
}')
#
echo "The result is $result"
#

./fact.sh 20
The result is 2,432,902,008,176,640,000
11. 创建 sed 实用工具

加倍行间距

向文本文件的行间插入空白行的简单sed脚本。

sed 'G' data2.txt
This is the header line.

This is the first data line.

This is the second data line.

This is the last line.

这个技巧的关键在于保持空间的默认值。还可以用排除符号(!)和尾行符号($)来确保脚本不会将空白行加到数据流的最后一行后面。

sed '$!G' data2.txt
This is the header line.

This is the first data line.

This is the second data line.

This is the last line.

对可能含有空白行的文件加倍行间距

sed '/^$/d ; $!G' data6.txt
This is line one.

This is line two.

This is line three.

This is line four.

给文件中的行编号

sed '=' data2.txt
1
This is the header line.
2
This is the first data line.
3
This is the second data line.
4
This is the last line.
sed '=' data2.txt | sed 'N; s/\n/ /'
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.
nl data2.txt
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.

cat -n data2.txt
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.

打印末尾行

sed -n '$p' data2.txt
This is the last line.

那么,如何用美元符来显示数据流末尾的若干行呢?答案是创建滚动窗口。显示文件的后10行。

cat data7.txt
This is line 1.
This is line 2.
This is line 3.
This is line 4.
This is line 5.
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.

sed '{
> :start
> $q ; N ; 11,$D
> b start
> }' data7.txt
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.

删除连续的空白行

区间是/./到/^$/。区间的开始地址会匹配任何含有至少一个字符的行。区间的结束地址会匹配一个空行。在这个区间内的行不会被删除。

cat data8.txt
This is line one.


This is line two.

This is line three.


This is line four.

sed '/./,/^$/!d' data8.txt
This is line one.

This is line two.

This is line three.

This is line four.

删除开头的空白行

cat data9.txt


This is line one.

This is line two.
$
$ sed '/./,$!d' data9.txt
This is line one.

This is line two.

删除结尾的空白行

cat data10.txt
This is the first line.
This is the second line.


sed '{
> :start
> /^\n*$/{$d ; N ; b start }
> }' data10.txt
This is the first line.
This is the second line.

删除 HTML 标签

cat data11.txt
<html>
<head>
<title>This is the page title</title>
</head>
<body>
<p>
This is the <b>first</b> line in the Web page.
This should provide some <i>useful</i>
information to use in our sed script.
</body>
</html>

会尽可能多的匹配,结果有些异常。

s/<.*>//g

可以使用

s/<[^>]*>//g
sed 's/<[^>]*>//g ; /^$/d' data11.txt
This is the page title
This is the first line in the Web page.
This should provide some useful
information to use in our sed script.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值