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

Tag*_*int 3 bash sed

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

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 表达式,然后使用-fswitch将它们传递给 sed

前任。

sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file
Run Code Online (Sandbox Code Playgroud)

这至少只调用 sed两次


Sté*_*las 6

随着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使用非零值。