在文件中搜索具有给定字符串的最后一行,以在该匹配项后添加一行。例如:
输入:
Apple
Banana
Apple
Orange
Apple
Grapefruit
输出:
Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Grapefruit
答案1
使用 GNUsed
你可以这样做:
sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'
当文件末尾 (地址 ) 未到达时 ( )(使用 跳转到) ,模式:a;N;$! ba
会将文件中的行附加到缓冲区。因此,如果退出循环,所有行都在缓冲区中,搜索模式会匹配整个文件,直到以 开头的最后一行。在替换字符串中插入整个匹配项并附加文本。N
$
!
:a
ba
.*\nApple
Apple
&
sed
适用于其他版本的便携式解决方案
sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&\1Your appended line/'
此处脚本在标签处被拆分,并且不使用\n
替换字符串(不能保证与非 GNU 一起使用),而是引用带有sed
引用的模式中的字符串。\(\)
\1
答案2
我看到了这个例子这里
sed -i -e "\$aTEXTTOEND" <filename>
信息:
i: edit files in place
e: add the script to the commands to be executed
$: sed address location
a: append command
TEXTTOEND: text to append to end of file
答案3
我假设您的示例输入是一个名为 的文件fruits.txt
。
如果您想在最后一次出现“Apple”之后插入一行,可以sed
这样做:
sed -rz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
该-r
选项启用扩展正则表达式。
该-z
选项指示sed
使用 NUL 字符(ASCII 代码 0)作为行分隔符,而不是普通的换行符。这样,整个文本文件将一次性处理,而不是逐行处理。
该sed
命令s/PATTERN/REPLACEMENT/
搜索(扩展,因为-r
)正则表达式PATTERN
并将其替换为评估的REPLACEMENT
字符串。
正则表达式(^(.*\n)?Apple)(\n|$)
匹配从开头 ( ^
) 到最后一个出现的行,Apple
后面跟着换行符 ( \n
) 或文件结尾 ( $
) 的任意数量的行。
现在整个匹配被替换为\1\ninserted line\n
,其中\1
代表第一个匹配组的原始内容,即直到的所有内容Apple
。因此实际上它在最后一次出现“Apple”之后插入inserted line
一个换行符( )。\n
如果“Apple”行是文件中的第一行、最后一行或唯一一行,这也有效。
示例输入文件(在示例中添加了一行,以使行为清晰):
Apple
Banana
Apple
Orange
Apple
Cherry
示例输出:
Apple
Banana
Apple
Orange
Apple
inserted line
Cherry
如果您对结果满意并且想要fruits.txt
就地编辑,而不是仅仅输出修改,您可以添加以下-i
选项:
sed -irz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'
但是,如果您只是想将一行文本附加到该文件的末尾,那么sed
这并不是最佳工具。
您可以简单地使用 Bash 的>>
附加输出重定向:
echo "I am new string replaced at last apple" >> fruits.txt
会将>>
左侧命令的标准输出(echo
此处的命令只是将其参数打印到标准输出)附加到右侧指定的文件中。如果该文件尚不存在,则会创建该文件。
答案4
在这个答案中:
- sed+tac(双输入反转)
- 关于java:AWK双重输入
- Perl 双重输入
- 具有双重输入反转的替代 Perl
sed + tac
这种方法使用 3 个过程,首先反转文件,使用 sed 查找第一个匹配项并在第一个匹配项后插入所需文本,然后再次反转文本。
$ tac input.txt | sed '0,/Apple/{s/Apple/NEW THING\n&/}' | tac
Apple
Banana
Apple
Orange
Apple
NEW THING
something else
something other entirely
blah
基本思想与下面写的替代 perl 版本相同:反向行列表中的第一个匹配项是原始列表中的最后一个匹配项。就用户而言,这种方法的优点是简单易读。
这种方法对于小文件效果很好,但对于大文件来说就很麻烦,并且可能需要花费相当长的时间来处理大量的行。
大王
对于您要做的事情,Awk 可能会更友好一些:
$ awk -v n="AFTER LAST APPLE" 'NR==FNR&&/Apple/{l=NR};NR!=FNR{if(FNR==l){print $0;print n}else print}' input.txt input.t>
Apple
Banana
Apple
Orange
Apple
AFTER LAST APPLE
something else
something other entirely
blah
这里的基本思想是将你的输入文件交给 awk 两次 - 第一次找到最后一个 Apple 的位置,第二次当我们传递从第一次迭代中找到的最后一个“苹果”的位置时插入文本。
实现这一目标的关键是评估NR
(不断计算所有已处理的行数、总数)和FNR
(当前文件中的行数)变量。当这两个文件不相等时,我们知道我们正在处理第二个文件,因此我们知道我们已经完成了第一次查找 Apple 最后一次出现的位置。
由于我们读取文件两次,性能将取决于文件的大小,但它比 sed + tac 组合更好,因为我们不需要反转文件两次。
Perl
perl -sne '$t+=1;$l=$. if /Apple/ and $.==$t;
if($t!=$.){if($.==$l){print $_ . $n . "\n";}else{print $_}};
close ARGV if eof;' -- -n="AFTER LAST APPLE" input.txt input.txt
perl 解决方案遵循与 AWK 相同的思想。我们传递两次输入,并使用第一次传递文件时查找 Apple 的最后一次出现位置并将其行号存储到$l
变量中。这里与 awk 的不同之处在于,perl 中没有FNR
变量,因此我们必须使用$t+=1
andclose ARGV if eof
来实现此目的。
这里的不同之处在于,-s
perl 允许你通过 switch 传递参数。在本例中,我们要插入的字符串通过-n
switch 传递。
替代 Perl
这是在 Perl 中执行此操作的另一种方法。其想法是简单地将文件读入行数组,然后反转该数组。反转数组中的第一个匹配项是原始行列表中的最后一个。因此,我们可以在该行之前插入所需的文本,然后再次反转列表:
$ perl -le '@a1=<>;END{do{push @a2,"NEW LINE\n" and $f=1 if $_ =~ /Apple/ and !$f; push @a2,$_} for reverse(@a1); print reverse(@a2)}' input.txt
Apple
Banana
Apple
Orange
Apple
NEW LINE
something else
something other entirely
blah