【问题标题】:Replace the "pattern" on second-to-last line of a file替换文件倒数第二行的“模式”
【发布时间】:2016-07-29 12:47:06
【问题描述】:

我必须在文件 file.txt 的倒数第二行用“字符串”替换“模式”。 以下三个 sed 命令能够打印倒数第二行。但我需要用“字符串”替换“模式”。有什么帮助吗??

sed -e '$!{h;d;}' -e x file.txt

sed -n 'x;$p' file.txt

sed 'x;$!d' file.txt

$ cat file.txt  
cabbage  
spinach  
collard greens  
corn salad  
Sweet pepper  
kale  

如何替换文件的倒数第二行(甜椒):
一个。如果倒数第二行包含“Sweet pepper”,则将“Sweet”替换为“green”
湾。用“carrots”替换整行,不管它包含什么

【问题讨论】:

  • sed 区分大小写,我已将sweet 更改为Sweet

标签: sed


【解决方案1】:

要在倒数第二行将 Sweet 更改为 Green,但前提是该行包含 Sweet pepper

$ sed 'x; ${/Sweet pepper/s/Sweet/Green/;p;x}; 1d' file.txt
cabbage
spinach
collard greens
corn salad
Green pepper
kale

将整个倒数第二行替换为胡萝卜,无论它包含什么内容:

$ sed 'x; ${s/.*/carrots/;p;x}; 1d' file.txt
cabbage
spinach
collard greens
corn salad
carrots
kale

工作原理

让我们使用这个命令,一步一步地检查它:

sed 'x; ${s/.*/carrots/;p;x}; 1d'
  • x

    这会交换模式空间(保存最近读取的行)和保存空间。

    完成后,保持空间将包含最近读取的行,而模式空间将包含前一行。 (例外情况是我们刚刚读取了第一行。在这种情况下,保持空间将有第一行,而模式空间将为空。)

  • ${s/.*/carrots/;p;x}

    当我们在最后一行时,由$ 指示,模式空间保存倒数第二行,我们可以执行任何替换或其他我们喜欢的命令。完成后,我们用p 打印倒数第二行。最后,我们用x 再次交换模式并保留空间,这样模式空间将再次包含最后一行。 sed 将打印此内容,因为默认情况下,在命令末尾,sed 打印模式空间中的任何内容。

  • 1d

    当我们在第一行时,由1 表示,模式空间是空的(因为没有前一行),我们将其删除(d)。

更简单的方法

这种方法很容易理解,代价是执行速度较慢:

$ tac file.txt | sed '2 {/Sweet pepper/s/Sweet/Green/}' | tac
cabbage
spinach
collard greens
corn salad
Green pepper
kale

还有,对于胡萝卜:

$ tac file.txt | sed '2 s/.*/carrots/' | tac
cabbage
spinach
collard greens
corn salad
carrots
kale

工作原理:在这里,我们使用tac 来颠倒行的顺序。观察:

$ tac file.txt
kale
Sweet pepper
corn salad
collard greens
spinach
cabbage

这样,倒数第二行变成了第 2 行。因此,我们只是简单地告诉 sed 对第 2 行进行操作。之后,我们再次使用tac 来放置这些行,但顺序正确。

【讨论】:

  • 我想到了tac,但你已经提出来了。 :) 也就是说,我也喜欢第一个解决方案 :) 所以这个东西是 ++。
  • 它有效。你简直太棒了,能够非常清楚准确地解释。坚持下去。
  • 另一种选择:sed 'N;$s/^Sweet pepper/green/;P;D' filesed 'N;$s/^[^\n]*/carrot/;P;D' file
【解决方案2】:

您可能会发现 awk 脚本更易于理解、维护、移植等:

$ awk 'NR==FNR{tgt=NR-1;next} (FNR==tgt) && /Sweet pepper/ { $1="green" } 1' file file
cabbage
spinach
collard greens
corn salad
green pepper
kale

$ awk 'NR==FNR{tgt=NR-1;next} (FNR==tgt) { $0="carrots" } 1' file file
cabbage
spinach
collard greens
corn salad
carrots
kale

想要更改结束前的第 3 行而不是结束前的第 1 行?这只是将-1 更改为-3 的简单而明显的调整:

$ awk 'NR==FNR{tgt=NR-3;next} (FNR==tgt) { $0="carrots" } 1' file file
cabbage
spinach
carrots
corn salad
Sweet pepper
kale

【讨论】:

  • 由于相同的file 被传递了两次,awk 是否足够聪明以存储单个副本?
  • @sjsam 如果操作系统足够小,它可能足够聪明,可以缓存它。鉴于此,优化 Awk 来做到这一点可能是不明智的。
  • @sjsam idk 但无论如何这一切都会在眨眼间发生,除非文件绝对巨大。
  • @EdMorton:您的解决方案也有效。感谢您提供了一个很好的答案,尤其是您提到的关于在结束前调整第 3 行的答案。坚持下去,先生。干得好。
【解决方案3】:

awk 解决方案

$ cat 38649053
cabbage
spinach
collard greens
corn salad
Sweet pepper
kale
$tac 38649053 | awk 'NR==2{if($0=="Sweet pepper")#is record sweet pepper?
{ 
$1="green"} #changing sweet to green , note $1 is the first field
else{
$0="carrot" # $0 is the whole record which replaced by carrot
}}
{record[NR]=$0} #adding each record to an record number-indexed array
END{ #printing the records in reverse at the end
i=NR;for(;i>=1;i--)print record[i]} 
'
cabbage
spinach
collard greens
corn salad
green pepper
kale

Sed 解决方案

$ cat 38649053
cabbage
spinach
collard greens
corn salad
Sweet pepper
kale 
$ lines=$(wc -l <38649053) # lines contains the total # of lines
$ ((s2l=--lines)) # storing the second to last line number to s2l
$ sed -E "${s2l}"'{/^Sweet pepper$/!s/.*/carrot/;/^Sweet pepper$/s/Sweet/green/}' 38649053
# applying the sed to the required line
cabbage
spinach
collard greens
corn salad
green pepper
kale 

【讨论】:

  • 只要程序员的时间比计算机的时间贵(几乎总是如此),那么tac 就大大简化了问题。 +1。
  • 当然您也可以轻松地将tacsed 一起使用。 tac file | sed '2s/Sweet pepper/green pepper/' | tac
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-27
  • 1970-01-01
  • 2018-01-21
  • 1970-01-01
  • 2014-08-12
  • 1970-01-01
  • 2014-09-12
相关资源
最近更新 更多