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 mitN
und 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/'
%s
Grundsätzlich sollte das Zeichen oder die Zeichenfolge, das/die die beiden Elemente in der Ausgabe trennt, in derprintf
Formatierungszeichenfolge danach und\1
im 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(.*)\1
ist 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 vonprint
und 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$i
auf 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 istfoo ? bar : baz
und bedeutet „wennfoo
wahr ist, dann führe ausbar
, sonst führe ausbaz
“. Hier testen wir, ob dasn
-te (oder$i
-te, in diesem Fall) Element des Arrays@A
dasselbe 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 mitlast
.print @S
: Drucken Sie das Array@S
und 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 ".", @S
ausgedruckt werden .@S
.
Antwort3
Wie ich in den Kommentaren unter der Frage erwähnt habe, habe ich eine relativ einfache awk
Lö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-8
Befehl 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
first
Die Variable enthält die Teile des ersten Elements der Eingabeliste, die bei.
(a[0].split
) aufgeteilt wurden. Ebensosecond
enthält sie die Teile des zweiten Elements der Listea
.Dann haben wir iteriert
first
undsecond
die Gleichheit jedes Elements mit seinem gleich indexierten Gegenstück überprüft. Wenn sie gleich sind, wird eines davon in einer separaten Liste gespeichertresult
. 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