在文件中最后一次匹配的搜索词出现之前添加文本

在文件中最后一次匹配的搜索词出现之前添加文本

我需要在多个文件中最后一次出现某个单词(在我的示例中为 SearchWord)之前添加几行文本。

  • 搜索应该不区分大小写。
  • 该单词出现在文件的多行中。

文件中的现有数据

SearchWord abc defgh
SEARCHWORD abc1234
sEarchWord abcd
sometext 1
sometext 2

所需输出

SearchWord abc defgh
SEARCHWORD abc1234 
NEWLINE_1_ADDED
NEWLINE_2_ADDED
NEWLINE_3_ADDED
sEarchWord abcd
sometext 1
sometext 2

答案1

使用exPOSIX 文件编辑器

ex是文件编辑器的非可视形式vi。它实际上早于vi. vi(和 Vim)仍然使用ex命令;任何时候你输入一个冒号并输入一个命令,那就是一个ex命令(尽管 Vim 有许多自定义扩展)。

自从ex打开所有的与 Sed 或 Awk 不同,寻址可以向后和向前进行,而不是在流中进行操作。

对于这一特定编辑,请使用以下一行:

printf '%s\n' 'set ignorecase' '$+?searchword?i' 'NEWLINE_1_ADDED' 'NEWLINE_2_ADDED' 'NEWLINE_3_ADDED' . x | ex input.txt

要测试命令(打印到标准输出而不是保存到文件),请使用%p而不是x作为最终命令:

printf '%s\n' 'set ignorecase' '$+?searchword?i' 'NEWLINE_1_ADDED' 'NEWLINE_2_ADDED' 'NEWLINE_3_ADDED' . %p | ex input.txt 

处理多个文件:

for f in *.txt; do
    printf '%s\n' 'set ignorecase' '$+?searchword?i' 'NEWLINE_1_ADDED' 'NEWLINE_2_ADDED' 'NEWLINE_3_ADDED' . x | ex "$f"
done

解释

单独运行该printf命令,您可以看到传递给的命令ex

set ignorecase
$+?searchword?i
NEWLINE_1_ADDED
NEWLINE_2_ADDED
NEWLINE_3_ADDED
.
x

set命令是不言自明的。

$+指的是线最后一行,?...?表示从那里向后搜索。 (这+使得具有匹配的最后一行的文件将被正确处理。) i意味着“插入”文本找到的线。

一行.本身结束要插入的文本。

x保存并退出。

答案2

如果您将这些行保存在文本文件中,例如infile然后用于ed就地编辑文件,则会更容易:

for f in ./*.txt; do
ed -s "$f" <<\IN
.t.
?[kK][eE][yY][wW][oO][rR][dD]?-1 r infile
$d
w
q
IN
done

如果您只想插入一些文本(而不是文件的内容),则类似:

for f in ./*.txt; do
ed -s "$f" <<\IN
.t.
?[kK][eE][yY][wW][oO][rR][dD]?-1 s/$\
some_text\
more_text\
last\\_\&_line
$d
w
q
IN

替换w,p以查看它在不修改文件的情况下执行的操作。
请注意,反斜杠、与号和分隔符必须在替换的右侧进行转义。这同样适用于换行符,除了最后一个。
怎么运行的 ?好吧,复制最后一行,向后搜索关键字,在该行之前插入文本或文件内容,删除重复的最后一行,写入,退出。


当然,如果您不需要就地编辑这些文件而只想打印修改后的版本,例如扎娜确实,您不需要具有另一个时代语法的神秘文本编辑器...您可以通过一次sed调用来完成它 - 因为通配符善意地提醒我,在这个编辑器系列中,有一个专用命令可以i在匹配之前插入文本,让我们使用它和一些分支:

sed -s '1{h;$!d;b end
}
/keyword/I{H;$!d
}
//{x;p;$!d
}
:end
${x;/keyword/Ii\
some_text_here\
more_text_here\
and_a\\_backslash
}' ./*.txt

这仅适用gnu sed于...对于其他seds,您必须运行一个循环(这次没有分支部分,以便在每个文件内容之间有一个空行):

for f in ./*.txt; do
sed -s '/[kK][eE][yY][wW][oO][rR][dD]/{H;$!d
}
//{x;p;$!d
}
${x;/[kK][eE][yY][wW][oO][rR][dD]/i\
some_text_here\
more_text_here\
and_a\\_backslash
}' "$f"
done

正如您所见,即使使用insert,仍然必须转义反斜杠和换行符(最后一个除外)。除此之外,它很简单:它只是在保留缓冲区中累积行,并在遇到匹配时进行交换;在最后一行,它再次进行交换并插入匹配之前的文本(如果有)。

答案3

如果你有tacGNUsed你可以使用

$ tac file | sed '0,/searchword/I s/.*searchword.*/&\nNEWLINE_3_ADDED\nNEWLINE_2_ADDED\nNEWLINE_1_ADDED/I' | tac
SearchWord abc defgh
SEARCHWORD abc1234
NEWLINE_1_ADDED
NEWLINE_2_ADDED
NEWLINE_3_ADDED
sEarchWord abcd
sometext 1
sometext 2

解释

  • tac向后打印文件
  • 0,/pattern/仅对第一次出现的模式进行操作
  • I不区分大小写的搜索
  • s/old/new/old用。。。来代替new
  • .*searchword.*匹配包含的整行searchword
  • &替换中的整个匹配模式
  • \n新队

也许更具可读性:

tac file | sed '0,/searchword/I s/.*searchword.*/&\
NEWLINE_ADDED_3\
NEWLINE_ADDED-2\
NEWLINE_ADDED_1/I' | tac

相关内容