我提前知道行号,并将它们保存在另一个文件中:
cat linenos
2
15
42
44
... etc
正如您所看到的,这些行不相邻,所以我不能使用一个范围sed
。目标是修改目标文件行,例如在目标文件行前面添加像 MARKER 这样的标记
sed
直接的方法是多次调用来修改每一行:
for l in $(cat linenos)
do
sed -i "${l}s/^/MARKER/" target_file
done
这显然会多次调用 sed 。
警告:*这种方法不仅效率低下,如果修改不是插入这样的标记,还可能导致出现问题。任何行删除或插入 sed 命令(如 dar)都会使 linenos 中的初始行号对于循环中的下一个 sed 运行无效。
您建议如何改进/优化?
示例 linenos 文件
cat linenos
2
5
示例目标文件
cat target_file
line one
line two
line three
line four
line five
line six
修改后的target_file的预期结果
cat target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
我想出的可能方法是动态创建 sed 场景
SEDCMD=$(for l in $(cat linenos); do echo -n "${l}s/^/MARK/;" ; done)
sed -i -e "$SEDCMD" targetfile
@steeldriver的以下方法与想法相同,但更加优雅和简洁
答案1
您可以使用 sed 本身(或您选择的其他文本处理实用程序)将行号转换为 sed 表达式,然后使用 switch 将它们传递给-f
sed
前任。
sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file
这至少只调用 sed两次。
答案2
与perl
(GNU 的来源sed
)-i
:
perl -pi -e '
BEGIN{$l{0+$_}=1 while <STDIN>}
$_ = "MARKER$_" if $l{$.}' target_file < linenos
perl
我们在的 stdin上提供行号列表。这是在BEGIN
块中读取的。
对于每一行输入,我们将该行转换为带有 的数字0+$_
。这使得换行符消失,并且规范化了数字(所有 1e0, 1, 01 都变成 1)。
哈希%l
表中填充了1
每个行号的值作为键。
target_file
在主-p
循环中进行处理,在主循环中将MARKERS
其添加到当前行号 ( $.
)%l
为非零值的行的前面。
答案3
$ awk 'NR==FNR{a[$1]="MARKER"; next} {print a[FNR] $0}' linenos target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
或者节省一点内存:
$ awk 'NR==FNR{a[$1]; next} {print (FNR in a ? "MARKER" : "") $0}' linenos target_file
line one
MARKERline two
line three
line four
MARKERline five
line six
如果您想要“就地”编辑(与 perl 和 GNU sed 相同-i
),请使用 GNU awk 并更改awk '...'
为并在语句之前awk -i inplace '...'
添加 a ,这样您的文件就不会被清空。恕我直言,使用任何 awk (或任何其他 UNIX 工具)执行此操作会更简单:print;
next
linenos
awk 'script' linenos target_file > tmp && mv tmp target_file
答案4
另一种方法,使用ed
而不是就地sed
修改target_file
:
(while IFS= read n; do echo "${n}s/^/MARKER/"; done < linenos; echo w) | ed -s target_file