簡體   English   中英

awk sed perl基於關鍵字匹配合並行

[英]awk sed perl to merge rows based on keyword match

由於我有限的awk / sed魔法,我在這個問題上一直在撞牆。 我很高興使用awk,sed,bash,perl或其他任何東西來完成這個文本操作。

我有以下輸出,並希望根據一種鍵匹配合並行:

 Node: server1
 Active Server: SECONDARY
 Standby Server: PRIMARY
 Primary 192.168.1.1
 Secondary 192.168.1.2

 Node: server2
 Active Server: PRIMARY
 Standby Server: SECONDARY
 Primary 10.1.1.1
 Secondary 10.1.1.2

期望的輸出:

 Node: server1
 Active Server: Secondary 192.168.1.2
 Standby Server: Primary 192.168.1.1

 Node: server2
 Active Server: Primary 10.1.1.1
 Standby Server: Secondary 10.1.1.2

所以我需要根據“primary”和“secondary”這兩個詞進行合並。 我的第一個想法是將“Primary”改為“PRIMARY”,這樣就更容易匹配了。

我最終的目標是擁有:

 server1,Active,192.168.1.2,Standby,192.168.1.1
 server2,Active,10.1.1.1,Standy,10.1.1.2

(但是我可以在幫助合並行之后弄清楚這部分)

謝謝您的幫助!

這個Perl解決方案似乎可以滿足您的要求。 它只是將值逐行拉入哈希值,並在存在所有必需值時轉儲哈希內容。

更新我使用了List::Util中的any代替grep來使代碼更清晰。

use strict;
use warnings;
use autodie;

use List::Util 'any';

my @names = qw/ node active standby primary secondary /;

open my $fh, '<', 'myfile.txt';

my %server;

while (my $line = <$fh>) {
  next unless my ($key, $val) = lc($line) =~ /(\w+).*\s+(\S+)/;

  %server = () if $key eq 'server';
  $server{$key} = $val;

  unless ( any { not exists $server{$_} } @names ) {
    printf "%s,Active,%s,Standby,%s\n", @server{'node', $server{active}, $server{standby}};
    %server = ();
  }
}

產量

server1,Active,192.168.1.2,Standby,192.168.1.1
server2,Active,10.1.1.1,Standby,10.1.1.2

它是密集且非常難看的多線程,

perl -00 -nE'
  s/ ^(\w+)\s+([\d.]+)\s* / $s{$1}=$2; ""/xmge;
  ($l=$_) =~ s! \s*\w+:\s*|\n !,!xg;
  $l =~ s|\U$_|$s{$_}| for keys %s;
  ($_=$l) =~ s/^,|,$//g;
  say
' file

產量

server1,Active,192.168.1.2,Standby,192.168.1.1
server2,Active,10.1.1.1,Standby,10.1.1.2

說明

# -00 => instead of single line read lines into $_ until \n\n+
perl -00 -nE'
  # read and remove 'Primary|Secondary IP' into $s{Primary} = IP
  s/ ^(\w+)\s+([\d.]+)\s* / $s{$1}=$2; ""/xmge;

  # replace 'something:' or new line by ','
  ($l=$_) =~ s! \s*\w+:\s*|\n !,!xg;

  # replace SECONDARY|PRIMARY with actual IP address
  $l =~ s|\U$_|$s{$_}| for keys %s;

  # remove ',' at beginning and end of the string
  ($_=$l) =~ s/^,|,$//g;

  # print result
  say
' file

或者使用單線程用於中間期望的解決方案(最終解決方案):

perl -00 -lpe '
     s/ Server: \K(\w+)(?=.*^(\1[^\n]*))/$2/ismg;
     s/\n[^:]+$//;
   ' file.txt

輸出:

Node: server1
Active Server: Secondary 192.168.1.2
Standby Server: Primary 192.168.1.1

Node: server2
Active Server: Primary 10.1.1.1
Standby Server: Secondary 10.1.1.2

說明:

  • 開關:
    • -00 :段落模式下的進程輸入(由雙返回分隔)
    • -l :啟用行結束處理
    • -p :假設"while (<>) { ...; print; }"循環程序
    • -e :評估perl代碼
  • 碼:
    • 使用以相同鍵開頭的匹配行替換所有服務器值
    • 刪除底部的服務器列表。

為了獲得您想要的最終解決方案,以下一個班輪將實現該目標。

第一個解決方案有一些細微的變化,例如使用-n而不是-p因為我們希望從記錄之間的兩個換行符移動到一個新行。 但是,正則表達式工具是相同的:

perl -00 -ne'
    s/ Server: (\w+)(?=.*^\1\s+(\S+))/:$2/ismg;
    s/\n[^:]+$//;
    s/^Node: //;
    s/[\n:]/,/g;
    print "$_\n";
  ' file.txt

輸出:

server1,Active,192.168.1.2,Standby,192.168.1.1
server2,Active,10.1.1.1,Standby,10.1.1.2
awk '
    $1 == "Active"  {active = tolower($NF); next} 
    $1 == "Standby" {standby = tolower($NF); next} 
    $1 == "Primary" {ip["primary"] = $0; next} 
    $1 == "Secondary" {
        ip["secondary"] = $0
        print "Active Server:",ip[active]
        print "Standby Server:",ip[standby]
        next
    }
    1
'

這假定“輔助”行位於“塊”的末尾。

要實現下一個輸出:

awk -v OFS="," '
    $1 == "Node:"   {node = $NF}
    $1 == "Active"  {active = tolower($NF)} 
    $1 == "Standby" {standby = tolower($NF)} 
    $1 == "Primary" {ip["primary"] = $2} 
    $1 == "Secondary" {
        ip["secondary"] = $2; 
        print node, "Active",ip[active],"Standup",ip[standby]
    }
'

回應jhill的評論:

awk -v RS="" -v OFS=, '{
    node = active = standby = ""
    delete ip
    for (i=1; i<NF; i++) {
        if      ($i == "Node:")     {node=$(++i)}
        else if ($i == "Active")    {active = tolower( $(i+=2) )}
        else if ($i == "Standby")   {standby = tolower( $(i+=2) )}
        else if ($i == "Primary")   {ip["primary"] = $(++i)}
        else if ($i == "Secondary") {ip["secondary"] = $(++i)}
    }
    print node, "Active", ip[active], "Standup", ip[standby]
}'

您可以使用tr消除空間,然后sed放回去,然后在正確的地方,並使用perl得到你想要的輸出:

輸入文件:

tiago@dell:/tmp$ cat file
 Node: server1
 Active Server: SECONDARY
 Standby Server: PRIMARY
 Primary 192.168.1.1
 Secondary 192.168.1.2

 Node: server2
 Active Server: PRIMARY
 Standby Server: SECONDARY
 Primary 10.1.1.1
 Secondary 10.1.1.2

腳本:

tiago@dell:/tmp$ cat test.sh 
#! /bin/bash

tr -d '\n' < $1 | sed -r 's/(Node:)/\n\1/g' |\
     perl -lne '
        /^\s+$/ && next;
        /Node:\s+(\w+.*?)\s/ && {$server=$1};
        /Active Server:\s+(\w+.*?)\s/ && {$active=$1};
        /Standby Server:\s+(\w+.*?)\s/ && {$standby=$1};
        /Primary\s+(\w+.*?)\s/ && {$pri=$1};
        /Secondary\s+(\w+.*?)\s/ && {$sec=$1};

        if ( "$active" eq "PRIMARY" ){
            $out="$server,Active,$pri,Standby,$sec";
        }else{
            $out="$server,Active,$sec,Standby,$pri";          
        }
        print $out;
    '

執行:

tiago@dell:/tmp$ bash test.sh  file 
server1,Active,192.168.1.2,Standby,192.168.1.1
server2,Active,10.1.1.1,Standby,192.168.1.2

你可以使用這個awk

awk -v RS="" '{$5=tolower($5);sub(".",substr(toupper($5),1,1),$5);$8=tolower($8);sub(".",substr(toupper($8),1,1),$8);print $1,$2"\n"$3,$4,$5,$10"\n",$6,$7,$8,$12}' file
Node: server1
Active Server: Secondary 192.168.1.1
 Standby Server: Primary 192.168.1.2
Node: server2
Active Server: Primary 10.1.1.1
 Standby Server: Secondary 10.1.1.2

通過sette將RS設置為空, awk與行組一起工作。

更冗長一點:

use strict;
use warnings;
use feature qw/say/;

my $struct;
local $/ = 'Node: ';


for my $record (<DATA>) {
    next if $record =~ /^Node:/; # skip first
    my ($node, @values) = split /\n\s*/, $record;
    for my $line (@values) { 
        my ($intent, $actual, $ip);
        if ( ($intent, $actual) = $line =~ /(Active|Standby) Server: (.*)$/ ) {
            $struct->{$node}{lc($intent)} = lc($actual);
        }
        elsif ( ($actual, $ip) = $line  =~ /(Primary|Secondary) (.*)$/ ) {
            $struct->{$node}{lc($actual)} = $ip;
        }
    }
}


for my $node (sort keys %$struct) {
    printf "Node: %s\n", $node;
    printf "Active server: %s %s\n", ucfirst $struct->{$node}{active}, $struct->{$node}{$struct->{$node}{active}};
    printf "Standby server: %s %s\n", ucfirst $struct->{$node}{standby}, $struct->{$node}{$struct->{$node}{standby}};
    print "\n";
}

## Desired final output is simpler:
for my $node (sort keys %$struct) {
    say join ',', $node, 'Active', $struct->{$node}{$struct->{$node}{active}}, 'Standby', $struct->{$node}{$struct->{$node}{standby}};
}


__DATA__
Node: server1
 Active Server: SECONDARY
 Standby Server: PRIMARY
 Primary 192.168.1.1
 Secondary 192.168.1.2

 Node: server2
 Active Server: PRIMARY
 Standby Server: SECONDARY
 Primary 10.1.1.1
 Secondary 10.1.1.2

這是awk中的一個選項。

#!/usr/bin/awk -f

# Output processing goes in a function, as it's called from different places
function spew() {
  split(servers[d["active"]], active);
  split(servers[d["standby"]], standby);
  printf("%s,%s,%s,%s,%s\n",
     d["name"], active[1], active[2], standby[1], standby[2]);
}

# trim unnecessary (leading) whitespace
1 { $1=$1; }

# Store our references
$1=="Active" {
  d["active"]=tolower($3);
}
#
$1=="Standby" {
  d["standby"]=tolower($3);
}

# And store our data
/^ *[A-za-z]+ [0-9.]+$/ {
  servers[tolower($1)]=tolower($0);
}

# Then, if we hit a new record, process the last one.
$1=="Node:" && length(d["name"]) {
  spew();
}

# And if we've just process a record, clear our workspace.
$1=="Node:" {
  delete d;
  delete s;
  d["name"]=$2;
}

# Finally, process the last record.
END {
  spew();
}

與其他一些解決方案相比,它的一個優點是它可以處理除“主要”和“次要”之外的名稱。 這個想法是,如果你有這樣的數據:

Node: serverN
Active Server: starfleet
Standby Server: babylon5
starfleet 172.16.0.1
babylon5 172.16.0.2

活動/備用線路將通過其索引引用記錄,而不是假設為“主要”或“次要”。

我已將所有內容標准化為小寫以便於處理,但您當然可以調整tolower()以適應。

awk ' s==0{print;s=1;next;}
      s==1{i=$0;s=2;next;}
      s==2{j=$0;s=3;next;}
      s==3{r1=$0;s=4;next;}
      s==4{r2=$0;
           sub(/SECONDARY/,r2,i);sub(/PRIMARY/,r1,j);
           sub(/SECONDARY/,r2,j);sub(/PRIMARY/,r1,i);
           s=5; print i;print j;next}
      s==5{s=0;print}' input.txt

輸出:

 Node: server1
 Active Server:  Secondary 192.168.1.2
 Standby Server:  Primary 192.168.1.1

 Node: server2
 Active Server:  Primary 10.1.1.1
 Standby Server:  Secondary 10.1.1.2

打印當前輸入節的第一行,將下四行存儲在變量中,然后進行替換,然后打印結果。 然后讀取並打印空行並再次開始下一部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM