如何使用sed在FIRST和LAST匹配模式之前插入一行

时间:2015-03-25 13:16:55

标签: linux bash unix awk sed

我有一个文件:users.txt,如下所示:

    Surname
    Surname
    Surname
    Age
    Age
    Age

我需要添加带有文字的 ONE 行:Name ABOVE ,无论我在哪里找到{{1>}的 FIRST 这样文件看起来像:

Surname

我还需要能够在文本中添加 ONE 一行: Name Surname Surname Surname Age Age Age BELOW ,无论我在哪里找到 LAST Gender以便文件如下:

Age

目前我有这些sed命令:

    Surname
    Surname
    Surname
    Age
    Age
    Age
    Gender

虽然他们在找到 EVERY 匹配项时添加了新行。

非常感谢您的时间和帮助。

6 个答案:

答案 0 :(得分:4)

awk会更容易。每当我听到“在最后做某事......”时,我认为“反转文件,并在FIRST上做点什么......”

file=users.txt
tmpfile=$(mktemp)

tac "$file" | 
awk '/Age/ && !found {print "Gender"; found=1} 1' | 
tac | 
awk '/Surname/ && !found {print "Name"; found=1} 1' > "$tmpfile" &&
mv "$tmpfile" "$file"
实际上sed不是那么残暴:

sed '
  /Surname/ {
    # we have seen the first pattern, insert the 1st new line
    i Name
    :a
    N                         # append the next line to pattern space
    $ {                       # if this is the last line
      s/.*Age\n/&Gender\n/    # add the 2nd new line after the last Age
      bb                      # and we are done: goto b
    }
    ba                        # goto a
    :b
  }
' users.txt 

答案 1 :(得分:1)

使用awk:

awk '/Surname/ && !s++ { print "Name" } /Age/ { a = 1; print; next } a && !f++ { print "Gender" } 1; END { if(a && !f) { print "Gender" } }' filename

其工作原理如下:

/Surname/ && !s++ {    # If Surname is found in a line and the flag for it
                       # is not set (i.e., if this is the first surname line),
                       # set flag and
  print "Name"         # print "Name"
}
/Age/ {                # if Age is found
  a = 1                # set flag for it
  print                # print the line
  next                 # and do nothing else for this line
}
a && !f++ {            # if the Age flag is set and (implicitly here) Age
                       # is not found in the line, and the flag that we
                       # already did this is not yet set, set the flag, and
  print "Gender"       # print Gender
}
1                      # then, finally, print the line from the input.

END {                  # if we reach the end without having printed the 
  if(a && !f) {        # gender line but found an Age block (that is, when the
                       # age block is at the end of the file)
    print "Gender"     # print it then.
  }
}

!s++内容是一种检查标志是否未设置并一次性设置的方法;我被告知这是惯用的awk。如果您对此感到不舒服,也可以写

/Surname/ && !s { s = 1; print "Name" }

等具有相同的效果。

答案 2 :(得分:1)

有很多方法可以做到这一点,但我喜欢的方法是坚固性,灵活性,清晰度,简单性,缺乏代码重复等,一次确定目标线,然后在第二次使用它们通过:

gawk '
    NR==FNR { if (!first[$0]) first[$0]=NR; last[$0]=NR; next }
    FNR==first["Surname"] { print "Name" }
    { print }
    FNR==last["Age"] { print "Gender" }
' file file

答案 3 :(得分:1)

如何在Perl中执行此操作

perl -nE'/Surname/&&($n++||say"Name")||($n=0);/Age/&&($g=1)||($g--&&say"Gender");print}{say"Gender"if$g'

另一种方式

perl -nE'/Surname/&&say("Name")..!/Surname/;/Age/&&($g=1)||($g--&&say"Gender");print}{say"Gender"if$g'

答案 4 :(得分:1)

这是如何在最后一次匹配之前插入带有 sed 的行。

sed -i -e '/**PATTERN**[^\n]*/,$!b;//{x;//p;g};//!H;$!d;x;i**TEXTOINSERT**' FILE

这是如何在第一次匹配之前用 sed 插入一行。

sed -i -e '/**PATERN**/{a\\**TEXTOINSERT**' -e ':a;n;ba}' FILE

答案 5 :(得分:0)

awk中
设置Surname第一次出现的记录编号和最后一次出现的年龄,读取文件两次并在达到设定的记录编号时打印。

awk '/Surname/ && !s++{Sr=NR}/Age/{FNR==NR&&age=NR}
     FNR!=NR{print (Sr==FNR?"Name\n"$0:age==FNR?$0"\nGender":$0)}' test{,}

Name
    Surname
    Surname
    Surname
    Age
    Age
    Age
Gender