两种模式之间的AWK - 第一次出现

时间:2014-12-31 04:03:22

标签: bash awk sed

我有这个ini文件的例子。我需要在两个模式Name_Z1和OBJ = Name_Z1之间提取名称,并将它们分别放在一行上。

问题是Name_Z1和OBJ = Name_Z1有多个出现,我只需要先出现。

[Name_Z5]
random;text
Names;Jesus;Tom;Miguel
random;text
OBJ=Name_Z5

[Name_Z1]
random;text
Names;Jhon;Alex;Smith
random;text
OBJ=Name_Z1

[Name_Z2]
random;text
Names;Chris;Mara;Iordana
random;text
OBJ=Name_Z2

[Name_Z1_Phone]
random;text
Names;Bill;Stan;Mike
random;text
OBJ=Name_Z1_Phone

我想要的输出是:

Jhon
Alex
Smith

我目前正在用bash编写一个更加丰富的脚本,我对此感到困惑。我更喜欢awk来完成这项工作。

我非常感谢谁能帮助我。谢谢!

对于Wintermute解决方案:[Name_Z1]部分如下所示:

[CAB_Z1]
READ_ONLY=false
FilterAttr=CeaseTime;blank|ObjectOfReference;contains;511047;512044;513008;593026;598326;CL5518;CL5521;CL5538;CL5612;CL5620|PerceivedSeverity;=;Critical;Major;Minor|ProbableCause;!=;HOUSE ALARM;IO DEVICE|ProblemText;contains;AIRE;ALIMENTA;BATER;CONVERTIDOR;DISTRIBUCION;FUEGO;HURTO;MAINS;MALLO;MAYOR;MENOR;PANEL;TEMP
NAME=CAB_Z1

[Name_Z1_Phone]部分如下所示:

[CAB_Z1_FUEGO]
READ_ONLY=false
FilterAttr=CeaseTime;blank|ObjectOfReference;contains;511047;512044;513008;593026;598326;CL5518;CL5521;CL5538;CL5612;CL5620|PerceivedSeverity;=;Critical;Major;Minor|ProbableCause;!=;HOUSE ALARM;IO DEVICE|ProblemText;contains;FUEGO
NAME=CAB_Z1_FUEGO

修复应该在" | PerceivedSeverity"

附近

预期产出:

511047
512044
513008
593026
598326
CL5518
CL5521
CL5538
CL5612
CL5620

6 个答案:

答案 0 :(得分:2)

这应该有效:

sed -n '/^\[Name_Z1/,/^OBJ=Name_Z1/ { /^Names/ { s/^Names;//; s/;/\n/g; p; q } }' foo.txt

说明:可读写,代码是

/^\[Name_Z1/,/^OBJ=Name_Z1/ {
  /^Names/ {
    s/^Names;//
    s/;/\n/g 
    p
    q
  }
}

这意味着:在模式范围/^\[Name_Z1/,/^OBJ=Name_Z1/中,对于与模式/^Names/匹配的所有行,请删除开头的Names;,然后将所有剩余的;替换为换行,打印整个内容,然后退出。由于它会立即退出,它只会在第一个这样的模式范围内处理第一条这样的线。

编辑:更新使事情变得复杂一些。我建议

sed -n '/^\[CAB_Z1/,/^NAME=CAB_Z1/ { /^FilterAttr=/ { s/^.*contains;\(.*\)|PerceivedSeverity.*$/\1/; s/;/\n/g; p; q } }' foo.txt

主要区别在于,不是从一行删除^Names,而是替换

s/^.*contains;\(.*\)|PerceivedSeverity.*$/\1/;

已应用。这会在继续之前将部分隔离在contains;|PerceivedSeverity之间。它假定该行中只有一个这样的部分。如果匹配不明确,它将选择行中最后一个匹配。

答案 1 :(得分:1)

sed -n '/\[Name_Z1\]/,/OBJ=Name_Z1$/ s/Names;//p' file.txt | tr ';' '\n'

这是sed -n以避免打印未明确请求的任何内容。从Name_Z1开始,在OBJ = Name_Z1处结束。删除名称;并打印出现的其余部分。最后,用换行符替换分号。

答案 2 :(得分:1)

Awk解决方案

$ awk -F";" '/Name_Z1/{f=1} f && /Names/{print $2,$3,$4} /OBJ=Name_Z1/{exit}' OFS="\n" input
Jhon
Alex
Smith

$ awk -F";" '/Name_Z1/{f++} f==1 && /Names/{print $2,$3,$4}' OFS="\n" input
Jhon
Alex
Smith

  • -F";"将字段分隔符设置为;

  • /Name_Z1/{f++}匹配带有模式/Name_Z1/的行如果匹配增量{f++}

  • 如果为真,则
  • f==1 && /Names/{print $2,$3,$4}if f == 1 and maches pattern Name with line相同,然后打印列2 3和4(由;分隔)

  • OFS="\n"将输出字段分隔符设置为\n新行


修改

$ awk -F"[;|]" '/Z1/{f++} f==1 && NF>1{for (i=5; i<15; i++)print $i}' input
511047
512044
513008
593026
598326
CL5518
CL5521
CL5538
CL5612
CL5620

答案 3 :(得分:1)

这是一组更通用的数据块组数据解决方案 这个awk不需要结束标记,只需要开头。

awk -vRS= -F"\n" '/^\[Name_Z1\]/ {n=split($3,a,";");for (i=2;i<=n;i++) print a[i];exit}' file
Jhon
Alex
Smith

工作原理:

awk -vRS= -F"\n" '      # By setting RS to nothing, one record equals one block. Then FS is set to one line as a field
/^\[Name_Z1\]/ {        # Search for block with [Name_Z1]
    n=split($3,a,";")   # Split field 3, the names and store number of fields in variable n
    for (i=2;i<=n;i++)  # Loop from second to last field
        print a[i]      # Print the fields
        exit            # Exits after first find
' file

使用更新的数据

cat file
data

[CAB_Z1_FUEGO]
READ_ONLY=false
FilterAttr=CeaseTime;blank|ObjectOfReference;contains;511047;512044;513008;593026;598326;CL5518;CL5521;CL5538;CL5612;CL5620|PerceivedSeverity;=;Critical;Major;Minor|ProbableCause;!=;HOUSE ALARM;IO DEVICE|ProblemText;contains;FUEGO
NAME=CAB_Z1_FUEGO

data

awk -vRS= -F"\n" '/^\[CAB_Z1_FUEGO\]/ {split($3,a,"|");n=split(a[2],b,";");for (i=3;i<=n;i++) print b[i]}' file
511047
512044
513008
593026
598326
CL5518
CL5521
CL5538
CL5612
CL5620

答案 4 :(得分:1)

(g)awk方式不需要一定数量的字段(虽然我假设contains;总是在你需要名字的行上。

(g)awk '(x+=/Z1/)&&match($0,/contains;([^|]+)/,a)&&gsub(";","\n",a[1]){print a[1];exit}' f

解释

(x+=/Z1/)                       - Increments x when Z1 is found. Also part of a 
                                  condition so x must exist to continue.

match($0,/contains;([^|]+)/,a)  - Matches contains; and then captures everything after 
                                  up to the |. Stores the capture in a. Again a 
                                  condition so must succeed to continue.

gsub(";","\n",a[1])             - Substitutes all the ; for newlines in the capture 
                                  group a[1].


{print a[1];exit}'              - If all conditions are met then print a[1] and exit.

这种方式适用于(m)awk

awk '(x+=/Z1/)&&/contains/{split($0,a,"|");y=split(a[2],b,";");for(i=3;i<=y;i++) 
      print b[i];exit}' file

答案 5 :(得分:0)

以下awk脚本将执行您想要的操作:

awk 's==1&&/^Names/{gsub("Names;","",$0);gsub(";","\n",$0);print}/^\[Name_Z1\]$/||/^OBJ=Name_Z1$/{s++}' inputFileName

更详细:

s==1 && /^Names;/ {
    gsub ("Names;","",$0);
    gsub(";","\n",$0);
    print
}
/^\[Name_Z1\]$/ || /^OBJ=Name_Z1$/ {
    s++
}

状态s以零值开头,并且只要您找到两行中的一行,就会递增:

[Name_Z1]
OBJ=Name_Z1

这意味着,在这些行的第一组之间,s将等于一。这是其他条件的来源。当s为1并且您找到以Names;开头的行时,您会进行两次替换。

第一种是摆脱前面的Names;,第二种是用换行符替换所有;分号字符。然后你打印出来。

您的测试数据的输出正如预期的那样:

Jhon
Alex
Smith