我提前知道行号,并将它们保存在另一个文件中:
cat linenos
2
15
42
44
... etc
Run Code Online (Sandbox Code Playgroud)
如您所见,这些线不相邻,因此我无法将一个范围用于sed
. 目标是修改目标文件行,例如,在它们前面加上一个标记,如 MARKER
直接的方法是sed
多次调用来修改每一行:
for l in $(cat linenos)
do
sed -i "${l}s/^/MARKER/" target_file
done
Run Code Online (Sandbox Code Playgroud)
这显然会多次调用 sed 。
注意:*这种方法不仅效率低下,而且如果修改不是插入这样的标记,它还会使事情出错。任何行删除或插入 sed 命令(如 dar)都会使 linenos 中的初始行号对于循环中的下一个 sed 运行无效。
你有什么建议来改进/优化?
示例 linenos 文件
cat linenos
2
5
Run Code Online (Sandbox Code Playgroud)
示例 target_file
cat target_file
line one
line two
line three
line four
line five
line six
Run Code Online (Sandbox Code Playgroud)
修改 target_file 的预期结果
cat target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
Run Code Online (Sandbox Code Playgroud)
我想出的可能方法是动态创建 sed 场景
SEDCMD=$(for l in $(cat linenos); do echo -n "${l}s/^/MARK/;" ; done)
sed -i -e "$SEDCMD" targetfile
Run Code Online (Sandbox Code Playgroud)
@steeldriver 的以下方法分享了这个想法,但更优雅和简洁
ste*_*ver 10
您可以使用 sed 本身(或您选择的其他文本处理实用程序)将行号转换为 sed 表达式,然后使用-f
switch将它们传递给 sed
前任。
sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file
Run Code Online (Sandbox Code Playgroud)
这至少只调用 sed两次。
随着perl
(其中GNUsed
得到了-i
来自):
perl -pi -e '
BEGIN{$l{0+$_}=1 while <STDIN>}
$_ = "MARKER$_" if $l{$.}' target_file < linenos
Run Code Online (Sandbox Code Playgroud)
我们在perl
的标准输入中输入行号列表。这是在BEGIN
块中读取的。
对于每一行输入,我们将该行转换为带有0+$_
. 这使得换行符消失并规范化数字(所有 1e0、1、01 都变为 1)。
的%l
哈希表被填充有值1
的每一行数作为密钥。
将target_file
在主-p
循环中处理,在其中找到MARKERS
当前行号 ( $.
) 的行之前%l
使用非零值。