我想使用 sed 在最后一行插入新字符串,并以一些文本开头,

我想使用 sed 在最后一行插入新字符串,并以一些文本开头,

在文件中搜索具有给定字符串的最后一行,以在该匹配项后添加一行。例如:

输入:

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$!:aba.*\nAppleApple&

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

在这个答案中:

  1. sed+tac(双输入反转)
  2. 关于java:AWK双重输入
  3. Perl 双重输入
  4. 具有双重输入反转的替代 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+=1andclose ARGV if eof来实现此目的。

这里的不同之处在于,-sperl 允许你通过 switch 传递参数。在本例中,我们要插入的字符串通过-nswitch 传递。

替代 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

相关内容