我需要在多个文件中最后一次出现某个单词(在我的示例中为 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
使用ex
POSIX 文件编辑器
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
于...对于其他sed
s,您必须运行一个循环(这次没有分支部分,以便在每个文件内容之间有一个空行):
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
正如您所见,即使使用i
nsert,仍然必须转义反斜杠和换行符(最后一个除外)。除此之外,它很简单:它只是在保留缓冲区中累积行,并在遇到匹配时进行交换;在最后一行,它再次进行交换并插入匹配之前的文本(如果有)。
答案3
如果你有tac
GNUsed
你可以使用
$ 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