修改由行号指定的非相邻行

修改由行号指定的非相邻行

我提前知道行号,并将它们保存在另一个文件中:

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 将它们传递给-fsed

前任。

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;nextlinenos

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

相关内容