'sed'替换最后一个模式并删除其他模式

时间:2018-11-13 20:36:37

标签: bash sed pattern-matching sh

我只想将文件中的最后一个字符串“ delay”替换为“ ens_delay”,并删除最后一个字符串之前的其他字符串:

输入文件:

alpha_notify_teta=''
alpha_notify_check='YES'
text='CRDS'
delay=''
delay=''
delay=''
textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''
alpha_orange='YES'
alpha_orange_interval='300'
alpha_notification_level='ALL'
expression='YES'
delay='9'
textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''

输出文件:(期望值)

alpha_notify_teta=''
alpha_notify_check='YES'
text='CRDS'

textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''
alpha_orange='YES'
alpha_orange_interval='300'
alpha_notification_level='ALL'
expression='YES'
ens_delay='9'
textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''

这是我的第一个命令,但是它不起作用,因为只有在我延迟到最后一行时它才起作用。

sed -e '$,/delay/ s/delay/ens_delay/'

我的第二个命令将删除所有包含“ delay”的行,甚至“ ens_delay”也将被删除。

sed -i '/delay/d'

谢谢

5 个答案:

答案 0 :(得分:3)

这可能对您有用(GNU sed):

sed '/^delay=/,$!b;/^delay=/!H;//{x;s/^[^\n]*\n\?//;/./p;x;h};$!d;x;s/^/ens_/' file

delay=开头的第一行之前的行应正常打印。否则,以delay=开头的行将存储在保留空间中,而没有以delay=开头的后续行将附加到该行。如果保留空间已经包含这些行,则删除第一行,并在保留空间之前用当前行替换剩余的行。在文件末尾,将保留空间的第一行修改为在字符串ens_之前加上前缀,然后打印整个保留空间。

答案 1 :(得分:0)

您无法使用sed做这种事情。 sed无法“向前看”并判断该模式是否还有更多匹配项。您可以回顾一下,但这不足以解决此问题。

此perl脚本将解决该问题:

#!/usr/bin/perl
use strict;
use warnings;
my ($seek, $replacement, $last, @new) = (shift, shift, 0);
open(my $fh, shift) or die $!;
my @l = <$fh>;
close($fh) or die $!;
foreach (reverse @l){
    if(/$seek/){
        if ($last++ == 0){
            s/$seek/$replacement/;
        } else {
            next;
        }
    }
    unshift(@new, $_);
}
print join "", @new;

致电方式:

./script delay= ens_delay= inputfile

我选择完全消除要删除的行,而不是将它们折叠成一个空白行。如果确实需要这样做,则要复杂一点:任何连续集中的第一行(或最后一行)都必须插入到输出列表中,并且您必须跟踪这是否刚刚完成,以便知道是否下次也要推。

您还可以使用awk,python或任何其他多种语言来解决此问题。只是不安。

答案 2 :(得分:0)

拥有这个怪物:

sed -e "1,$(expr $(sed -n '/^delay=/=' your_file.txt | tail -1) - 1)"'s/^delay=.*$//' \
    -e 's/^delay=/ens_delay=/' your_file.txt

这里:

  • sed -n '/^delay=/=' your_file.txt | tail -1返回遇到的模式的最后一行 number (我们将其命名为X
  • expr用于获取X-1
  • "1,X-1"'[command]'的意思是“在第一行和X-1行之间执行此命令(我使用双引号使扩展完成)
  • 's/^delay=.*$//'所说的[command]
  • -e 's/^delay=/ens_delay=/'下一个要执行的表达式(仅在最后一行出现)

输出:

alpha_notify_teta=''
alpha_notify_check='YES'
text='CRDS'



textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''
alpha_hsm_backup_notification='YES'
alpha_orange='YES'
alpha_orange_interval='300'
alpha_notification_level='ALL'
expression='YES'
ens_delay='9'
textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''
alpha_hsm_backup_notification='YES'

如果您要删除行而不是将其留空:

sed -e "1,$(expr $(sed -n '/^delay=/=' your_file.txt | tail -1) - 1)"'{/^delay=.*$/d}' \
    -e 's/^delay=/ens_delay=/' your_file.txt

答案 3 :(得分:0)

正如其他地方所提到的,sed无法知道哪个子字符串是最后一个子字符串。但是awk可以跟踪数组中的内容。例如,以下内容将删除所有重复的作业,并要求您进行替换:

awk 'BEGIN{FS=OFS="="} $1=="delay"{$1="ens_delay"} !($1 in a){o[++i]=$1} {a[$1]=$0} END{for(x=0;x<i;x++) printf "%s\n",a[o[x]]}' inputfile

或者,为了便于阅读/注释而进行了分拆:

BEGIN {
  FS=OFS="="       # set the field separator, to help isolate the left hand side
}

$1=="delay" {
  $1="ens_delay"   # your field substitution
}

!($1 in a) {
  o[++i]=$1        # if we haven't seen this variable, record its position
}

{
  a[$1]=$0         # record the value of the last-seen occurrence of this variable
}

END {
  for (x=0;x<i;x++)          # step through the array,
    printf "%s\n",a[o[x]]    # printing the last-seen values, in the order
}                            # their variable was first seen in the input file.

您可能不在乎变量的顺序。如果是这样,则以下内容可能会更简单:

awk 'BEGIN{FS=OFS="="} $1=="delay"{$1="ens_delay"} {o[$1]=$0} END{for(i in o) printf "%s\n", o[i]}' inputfile

这只是将最后看到的行存储在键为变量名的数组中,然后以未知顺序打印出数组的内容。

答案 4 :(得分:0)

假设我正确理解了您的规格,这应该可以满足您的需求。鉴于文件x

$: last=$( grep -n delay x|tail -1|sed 's/:.*//' )

grep是所有带有delay的行的文件,并以带冒号的行号返回它们。 tail -1抓住了这些行的最后一行,而忽略了所有其他行。 sed 's/:.*//'去除冒号和实际行内容,仅保留数字(此处为14。)

所有评估结果都将14分配为$last

$: sed '/delay/ { '$last'!d; '$last' s/delay/ens_delay/; }' x
alpha_notify_teta=''
alpha_notify_check='YES'
text='CRDS'
textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''
alpha_orange='YES'
alpha_orange_interval='300'
alpha_notification_level='ALL'
expression='YES'
ens_delay='9'
textfileooooop=''
alpha_enable='YES'
alpha_hostnames=''

不好意思的道歉。这样做是使用$last的值写入脚本,以便结果对sed如下所示:

$: sed '/delay/ { 14!d; 14 s/delay/ens_delay/; }' x

sed读取前导数字作为行选择器,因此此命令脚本的作用-

首先,sed会自动打印行,除非告知您不这样做,因此默认情况下它将只打印每行。该脚本对此进行了修改。

/delay/ { ... }是基于模式的记录选择器。它将{}之间的命令应用于与/delay/匹配的所有行,这就是为什么它不需要另一个grep的原因-它自己处理。在curl内,脚本执行两件事。

首先,14!d说(仅当该行具有delay时,才表示)如果行号为14,则不要 ({{1} })!删除记录。由于其他所有带有d的行都不会成为第14行(或先前命令创建的最后一个的任何值),因此这些行将被delay删除,这将自动重新启动循环并读取下一个记录。

第二,如果行号 为14,则不会d删除,因此将前进到d,这将更新您的值。

对于所有与s/delay/ens_delay/不匹配的行,/delay/仅按原样打印它们。