Vergleichen Sie 2 Ziffern und kopieren Sie nur den ähnlichen Teil sed/grep/awk

Vergleichen Sie 2 Ziffern und kopieren Sie nur den ähnlichen Teil sed/grep/awk

Angenommen, ich habe ein Array namens a. Es gibt 2 Einträge in einem Array a[1]und a[2]. Jedes Element enthält also einen numerischen Wert. Beide Werte haben ähnliche Startnummern, jedoch unterschiedliche Endungen. Ich soll den ähnlichen Teil kopieren und den Rest ignorieren.

AlsBeispiel

$ echo ${a[1]}
.1.3.6.1.4.1.232.13600256

$ echo ${a[2]}
.1.3.6.1.4.1.232.13600276

Ich brauche einen Befehl, um diese Elemente zu vergleichen und dann nur den ähnlichen Teil zu kopierenbis zum ersten nicht übereinstimmenden Feld. d. h. in diesem Beispiel

AUSGABE

similar part is .1.3.6.1.4.1.232

Ein anderes Beispiel

$ echo ${a[1]}
.1.3.6.1.4.1.759.2344.454545

$ echo ${a[2]}
.1.3.6.1.4.1.759.3234.454545

AUSGABE für dieses Beispiel

similar part is .1.3.6.1.4.1.759

Antwort1

AusPaketüberfluss:

In sed, vorausgesetzt, die Zeichenfolgen enthalten keine Zeilenumbruchzeichen:

string1="test toast"
string2="test test"
printf "%s\n%s\n" "$string1" "$string2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'

Dies setzt voraus, dass die Zeichenfolgen selbst keine Zeilenumbrüche enthalten.

Daher können Sie Folgendes tun:

printf "%s\n" "${a[1]}" "${a[2]}" | sed -r 'N;s/^(.*)(\..*)?\n\1.*$/\1/'

Der(\..*) sollenEntfernen Sie ein Trailing .aus dem gemeinsamen Abschnitt.


Die Lösung besteht aus zwei Teilen:

  • Arbeiten sedüber zwei Zeilen hinweg. Dies geschieht mit Nund kann vermieden werden, wenn garantiert ist, dass ein Zeichen nicht in der Eingabe enthalten ist. Da beispielsweise in den angegebenen Elementen keine Leerzeichen vorhanden sind, können wir stattdessen Folgendes verwenden:

    printf "%s " "${a[1]}" "${a[2]}" | sed -r 's/^(.*)(\..*)? \1.*$/\1/'
    

    %sGrundsätzlich sollte das Zeichen oder die Zeichenfolge, das/die die beiden Elemente in der Ausgabe trennt, in der printfFormatierungszeichenfolge danach und \1im regulären Ausdruck davor verwendet werden .

  • Suchen einer sich wiederholenden Zeichenfolge mithilfe von regulären Ausdrücken. Der Trick hierfür ist bekannt und besteht immer aus einer Variation von:

    (.*)\1
    

    .*passt zu jedem beliebigen Satz von Zeichen und ()gruppiert sie zur späteren Bezugnahme nach \1. Dies (.*)\1ist jede beliebige Zeichenfolge, gefolgt von sich selbst.

Antwort2

Hier ist eine Perl-Methode. Die Idee besteht darin, beide Eingabezeichenfolgen in separate Arrays aufzuteilen und über die Arrays zu iterieren, wobei alle Einträge gespeichert werden, die in beiden identisch sind:

perl -le '@A=split(//,$ARGV[0]);@B=split(//,$ARGV[1]); 
          for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last} 
          print @S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759.

Dies schließt jedoch das nachfolgende mit ein .. Ihre Ausgabe tut dies nicht (obwohl sie in beiden Variablen gleich ist). Wenn Sie es also entfernen möchten, verwenden Sie stattdessen Folgendes:

$ perl -le '@A=split(/\./,$ARGV[0]);@B=split(/\./,$ARGV[1]); 
            for $i (0..$#A){$A[$i] eq $B[$i] ? push @S,$A[$i] : last} 
            print join ".",@S' "${a[0]}" "${a[1]}"
.1.3.6.1.4.1.759

Erläuterung

  • -le: füge einen neuen hinzumine zu jedem Aufruf von printund führen Sie das von angegebene Skript aus -e.
  • @A=split(//,$ARGV[0]): $ARGV[0]ist das erste Argument, das in der Befehlszeile angegeben wird. Dadurch wird es aufgeteilt, sodass jedes Zeichen ein Element im Array ist @A.
  • @B=split(//,$ARGV[1]);: wie oben, aber für das 2. Argument und das Array @B.
  • for $i (0..$#A): eine for-Schleife. Diese setzt $iauf 0 und erhöht es um eins, bis es den Wert der Anzahl der Elemente in Array @A( $#A) hat. Dies ist eine einfache Möglichkeit, über alle Elemente in einem Array zu iterieren, da , , ... , $A[$i]sein werden .$A[0]$A[1]$A[$#A]
  • $A[$i] eq $B[$i] ? push @S,$A[$i] : last: Dies ist eine Kurzschreibweise im C-Stil. Das allgemeine Format ist foo ? bar : bazund bedeutet „wenn foowahr ist, dann führe aus bar, sonst führe aus baz“. Hier testen wir, ob das n-te (oder $i-te, in diesem Fall) Element des Arrays @Adasselbe ist wie das entsprechende aus dem Array @B. Wenn ja, fügen wir es dem dritten Array hinzu, @S. Wenn nicht, verlassen wir die Schleife mit last.
  • print @S: Drucken Sie das Array @Sund die gemeinsam genutzten Elemente.

Die beiden Lösungen sind sehr ähnlich. Der einzige Unterschied besteht darin, dass @A=split(/\./,$ARGV[0])bei aufgeteilt wird ., sie aus dem resultierenden Array entfernt und alle Elemente von mit einem dazwischen print join ".", @Sausgedruckt werden .@S.

Antwort3

Wie ich in den Kommentaren unter der Frage erwähnt habe, habe ich eine relativ einfache awkLösung gefunden: Verketten Sie die beiden Zahlen zu einer langen Zeichenfolge, ersetzen Sie alle Punkte durch Leerzeichen (um die Verwendung von Leerzeichen als Standardfeldtrennzeichen in awk zu ermöglichen) und gehen Sie die Zeichenfolge durch, indem Sie das Feld mit Datei+Hälfte vergleichen.

Grundlegender Befehl

printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'

Ich habe dies mit Gawk und Mawk getestet, es hat mit beiden funktioniert.

Hier ist die Ausgabe mit dem ersten Beispiel ( .1.3.6.1.4.1.232.13600256 und .1.3.6.1.4.1.232.13600276 ) :

$ printf ${a[1]}${a[2]} | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x };}'
.1.3.6.1.4.1.232

Mehrere Vergleiche

Wenn Sie mehrere Zeichenfolgen gleichzeitig vergleichen möchten, verknüpfen Sie sie miteinander und trennen Sie sie mit einer neuen Zeile in printf. Fügen Sie dann printf am Ende des awk-Befehls wie folgt hinzu:

printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'

Ausgabe:

$ printf "${a[1]}${a[2]}\n${a[3]}${a[4]}" | awk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}'
.1.3.6.1.4.1.232 # same for a[1] and a[2]
.1.3.6.1.4.1.759 # same for a[3] and a[4]

Begrenzung der Ausgabe

Nun hat Kos in seinem Kommentar richtigerweise darauf hingewiesen, dass OP nur 7 Zahlen anzeigen möchte. Zu diesem Zweck können Sie dem cut -d'.' -f1-8Befehl ein Pipe-Zeichen hinzufügen. So:

printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8

Hier ist eine Beispielausgabe von meinem Terminal:

$ a[5]=.1.3.6.1.4.1.232.13600256.885


$ a[6]=.1.3.6.1.4.1.232.13600256.885


$ printf "${a[5]}${a[6]}" | mawk '{gsub("\\."," "); half=NF/2}; { for ( x=1; x<=half; x++ ) { if ( $x==$(x + half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8
.1.3.6.1.4.1.232.13600256.885


 half) ) printf "."$x }; printf "\n"}' | cut -d'.' -f1-8                      <
.1.3.6.1.4.1.232

Noch mehr Vereinfachung

Auch hier kann alles in ein Awk-Skript geschrieben werden

#!/usr/bin/awk -f

{
 gsub("\\."," "); 
 half=NF/2
}; 

{ 
 for ( x=1; x<=half; x++ ) { 
    if ( $x==$(x + half) ) printf "."$x 
  }; 
  printf "\n"
}

Beispiellauf:

$ printf "${a[5]}${a[6]}" | num-comp.awk | cut -d'.' -f1-8                     
.1.3.6.1.4.1.232

Vergleich bis zur ersten ungleichen Zahl

Awk hat eine sehr nützliche Funktion substr(string,X,Y), die es ermöglicht, eine Zeichenfolge vom ersten Zeichen (x) bis zum Ende (Y) auszuschneiden oder zu „beschneiden“. Wenn wir das wissen, nehmen wir die beiden Zahlen als zwei Felder einer Zeichenfolge und führen sie durch eine While-Schleife. Wir werden die Teilzeichenfolgenlänge (von Anfang bis Ende) weiter erhöhen, bis sie nicht mehr gleich sind. Sobald wir auf die ungleichen Teilzeichenfolgen stoßen, beenden wir und drucken die letzte bekannte gleiche Teilzeichenfolge.

echo ".1.3.6.1.4.1.232.13600256\t.1.3.6.1.4.1.232.13600276" | awk 'BEGIN{i=1}{ while(substr($1,1,i)==substr($2,1,i)){var=substr($1,1,i);i++};} END{print var}'

Besonderer Dank geht an terdon für den Vorschlag, die Funktion substr zu verwenden, von der ich vorher nicht einmal wusste, dass sie existiert

Antwort4

Sie können eine kleine Funktion definieren python, die diese Aufgabe übernimmt:

#!/usr/bin/env python2
import itertools
def common_portion(a):
    first = a[0].split('.')
    second = a[1].split('.')
    result = []
    for (i, j) in itertools.izip(first, second):
        if i == j:
            result.append(i)
        else:
            break
    return 'Similar part is ' + '.'.join(result)
  • Wir müssen eine Liste mit den Zeichenfolgen bereitstellen, die wir als Eingabe für die Funktion überprüfen möchten

  • firstDie Variable enthält die Teile des ersten Elements der Eingabeliste, die bei .( a[0].split) aufgeteilt wurden. Ebenso secondenthält sie die Teile des zweiten Elements der Liste a.

  • Dann haben wir iteriert firstund seconddie Gleichheit jedes Elements mit seinem gleich indexierten Gegenstück überprüft. Wenn sie gleich sind, wird eines davon in einer separaten Liste gespeichert result. Immer wenn wir auf den ersten Unterschied gestoßen sind, sind wir aus der Schleife ausgebrochen.

  • .Zum Schluss haben wir unser gewünschtes Ergebnis durch Verbinden der Felder mit s( '.'.join(result)) ausgegeben.

Prüfen :

print common_portion(['.1.3.6.1.4.1.232.13600256', '.1.3.6.1.4.1.232.13600276'])

Similar part is .1.3.6.1.4.1.232


print common_portion(['.1.3.6.1.4.1.759.2344.454545', '.1.3.6.1.4.1.759.3234.454545'])

Similar part is .1.3.6.1.4.1.759

verwandte Informationen