以下是我要格式化的示例文本块:
<tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr>
<tr><td></td><td class="tdci">The raging rocks</td><td></td></tr>
<tr><td></td><td class="tdci">The foolish Fates.</td></tr>
<tr><td></td><td>This was lofty! Now name the rest of the players.</td><td></td></tr>
使用这两个&#39; sed&#39;脚本中的命令:
sed -ri '/^<tr><td><\/td><td>/N;s/(\n<tr><td><\/td><td class="tdci">)/\n<tr><td>\ <\/td><\/tr>\1/' "$f" #insert table row with empty data fields (blank line) above first line with 'class="tdci"'
sed -ri '/^<tr><td><\/td><td class="tdci">/N;s/(\n<tr><td><\/td><td>)/\n<tr><td>\ <\/td><\/tr>\1/' "$f" #insert table row with empty data fields (blank line) after last line with 'class="tdci"'
结果如下:
<tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr>
<tr><td> </td></tr>
<tr><td></td><td class="tdci">The raging rocks</td><td></td></tr>
<tr><td></td><td class="tdci">The foolish Fates.</td></tr>
<tr><td></td><td>This was lofty! Now name the rest of the players.</td><td></td></tr>
所以第一个sed
命令的工作原理是在class="tdci"
的第一行上面插入一个空白表行,但几乎相同的第二个sed
命令意味着在第一行之后插入一个空行表行。 <{1}}的最后一行不起作用。
我通常保存这些类型的编辑,在多行之间进行编辑,因为我从来没有遇到类似命令的问题,但出于某种原因class="tdci"
&#34; s&#34;对于我来说sed
一直都很受欢迎,就像在这个例子中,一个实例工作正常,而第二个实例没有。该脚本在这些命令运行之前删除所有前导/尾随空格和任何Winblowz回车符(N;s/
)。
由于我要编辑大量文件,我当然更愿意让这个文件在脚本中工作,如果有人能够看到任何明显的我做错了。
其他详细信息:
抱歉,我忘了提到我在Linux中运行\r
(Debian stable)
答案 0 :(得分:5)
从小处开始!对于您正在做的事情,这是一个更简单的测试案例:
a1
b1
b2
a2
以下是为此测试用例翻译的代码,尝试在第一个“b”之前插入c1
,在最后一个之后插入c2
:
sed -ri '/a/N; s/(\nb)/\nc1\1/' file
sed -ri '/b/N; s/(\na)/\nc2\1/' file
第一个命令,就像你说的那样,似乎有效:
a1
c1
b1
b2
a1
第二个没有,只是给你与上面相同的结果,而不是插入c2
。
以下是您可能认为会发生的事情,粗体部分不正确:
a1
被阅读并打印。c1
被阅读并打印。b1
已被阅读。
/b/
匹配,b2
与N
一起阅读。 \na
不符。 b1
已打印 b2
第二次阅读。
/b/
匹配,a
与N
一起阅读。\na
。附加c2
。b2\nc2\na
已打印。以下是实际发生的事情,
a1
被阅读并打印。c1
被阅读并打印。b1
已被阅读。
/b/
匹配,b2
与N
一起阅读。 \na
不符。 b1\nb2
已打印 a2
已阅读并打印,因为上面已经阅读了b2
。 这是一个有效的命令:
sed -ri '/b/ { :b; N; s/\na/\nc2&/; te; P; D; bb; }; :e;' file
在伪代码中 - 在评论中大致相应的sed部分 - 这是:
if (input.matches("b")) { // /b/ {
while(true) { // :b
input += "\n" + readline(); // N
if(input.matches("\na")) { // s/\na/ ..
input = input.replace("(\na)", "\nc2\1"); // .. \nc2&/
goto exit; // te
}
print(input.substring(0, input.indexOf('\n')); // P
input = input.substring(input.indexOf('\n') + 1); // D
} // bb
} // }
:exit // :e
翻译回您的数据:
sed -ri '/^<tr><td><\/td><td class="tdci">/ { :b; N; s/(\n<tr><td><\/td><td>)/\n<tr><td>\ <\/td><\/tr>\1/; te; P; D; bb; }; :e' "$f"
答案 1 :(得分:2)
@that other guy's excellent answer显示了如何使用sed
。
然而,sed
可能是一个大脑弯曲,当涉及这些问题时,这些问题本质上是程序性的,所以这里的 awk
解决方案可能更容易理解强>:
awk -v blockRegex='^<tr><td><\/td><td class="tdci">' \
-v lineToInsert='<tr><td>\ <\/td><\/tr>' \
'
# Print a line BEFORE the FIRST line matching `blockRegex`.
$0 ~ blockRegex { if (!afterFirst) {print lineToInsert; afterFirst=inBlock=1} }
# Print a line AFTER the LAST (contiguous) line matching `blockRegex`.
inBlock && $0 !~ blockRegex { print lineToInsert; afterFirst=inBlock=0 }
# Print the input line.
{ print }
' \
file
请注意,这可以进一步优化,但我希望更简单地澄清逻辑。
blockRegex
作为变量(带有选项-v
)传递给识别要插入行的前后行的连续行 - 带有该行要插入作为变量lineToInsert
传入。$0 ~ blockRegex
匹配感兴趣的行块中的每一行,如果它是块中的第一行行,则打印要插入的行,如状态变量{ {1}};状态变量afterFirst
表示手头的线在感兴趣的块内。inBlock
匹配感兴趣的块之后的第一行行并打印要插入的行,然后重置状态变量。 inBlock && $0 !~ blockRegex
只是按原样打印输入行。请注意,状态变量的使用依赖于print
中未初始化的变量默认为awk
(在布尔上下文中被视为0
;类似地,非零值评估为false
)。