String Unterschied in Bash

110

Ich versuche einen Weg zu finden, um den Unterschied zwischen zwei Zeichenfolgen in meinem Skript zu bestimmen. Ich könnte dies leicht mit diff oder comm tun, aber ich habe es nicht mit Dateien zu tun und ich würde es vorziehen, sie nicht in Dateien auszugeben, zu vergleichen und zurückzulesen.

Ich sehe, dass comm, diff, cmp alle erlauben, entweder zwei Dateien ODER eine Datei und eine Standardeingabe zu übergeben - ich denke, das ist gut, wenn ich nicht zwei Dateien ausgeben möchte ... aber es ist immer noch ein bisschen beschissen.

Ich habe herumgegraben und gedacht, ich kann grep oder reguläre Ausdrücke verwenden - aber ich denke nicht.

Codeforester
quelle
1
Was möchten Sie eigentlich tun?
Sie können Teilstring-Manipulationen und integrierte Testoperationen mit IFS-Änderungen zum Vergleichen verwenden, aber Sie müssen wissen, ob Sie Zeichen für Zeichen, Wort für Wort, Zeile für Zeile vergleichen möchten, Leerzeichen ignorieren
möchten

Antworten:

198

Verwenden diffoder comoder was auch immer Sie wollen:

diff  <(echo "$string1" ) <(echo "$string2")

Greg's Bash FAQ: Prozessersetzung

oder mit einem benannten Rohr

mkfifo ./p
diff - p <<< "$string1" & echo "$string2" > p

Greg's Bash FAQ: Arbeiten mit Named Pipes

Named Pipe wird auch als FIFO bezeichnet.

Das -allein ist für die Standardeingabe.

<<< ist eine "hier Zeichenfolge".

&ist wie ;, stellt es aber in den Hintergrund

Ian Kelling
quelle
5
+1 für die richtige Antwort. +1 für eine gute Erklärung der Symbole. Außerdem wurde Gregs Bash-FAQ verschoben zu: mywiki.wooledge.org Die Links zu den oben genannten Seiten befinden sich jetzt unter mywiki.wooledge.org/ProcessSubstitution und mywiki.wooledge.org/BashFAQ/085
timemachine3030
Vielen Dank! und dies zeigt auch die dynamischen DateideskriptorenFUNC(){ echo "$@"; "$@"; }; FUNC diff <(echo a) <(echo b);
Aquarius Power
Ich habe danach gesucht, um zwei Shasums zu vergleichen. Ich bin mir nicht sicher, ob es einen eleganteren Weg gibt, aber es funktioniert.
Fuma
Dies scheint zu funktionieren, wenn in $ string1 und $ string2 mehrere Zeilen vorhanden sind und diff die hinzugefügten oder subtrahierten Zeilen ausgibt. Was ist, wenn die Zeichenfolge eine einzelne Zeile und eine Zeile ist und es einen Unterschied zwischen den beiden Zeichenfolgen gibt?
Alpha_989
@ alpha_989, hier ist Ihre Antwort: Die $ diff <(echo "Here are the letters in String One.") <(echo "Here are the characters in String Two.") \n 1c1 \n < Here are the letters in String One. \n --- \n > Here are the characters in String Two. \nVerwendung der Pipe ist ähnlich, außer dass sie eine Prozessnummer anzeigt, mit der 1c1nach der nächsten beginnt $und wartet, bis Sie <kbd> drücken. <kbd> eingeben (oder Sie können andere Befehle ausführen ...)
bballdave025
19

Erinnert mich an diese Frage: Wie können Sie zwei Pipelines in Bash unterscheiden?

Wenn Sie sich in einer Bash-Sitzung befinden, können Sie Folgendes tun:

diff <cmd1 <cmd2
diff <(foo | bar) <(baz | quux)

Durch das <Erstellen anonymer Named Pipes, die von Bash verwaltet werden, werden sie im Gegensatz zu temporären Dateien automatisch erstellt und zerstört.

Wenn Sie es also schaffen, Ihre zwei verschiedenen Zeichenfolgen als Teil eines Befehls (grep, awk, sed, ...) zu isolieren, können Sie beispielsweise Folgendes tun:

diff < grep string1 myFile < grep string2 myFile

(Wenn Sie annehmen, dass Sie in Ihren Dateizeilen Folgendes haben string1=very_complicated_valueund a string2=another_long_and_complicated_value': Ohne das interne Format Ihrer Datei zu kennen, kann ich keinen genauen Befehl empfehlen.)

VonC
quelle
13

Ich bevorzuge cmpund Process Substitution Feature von Bash:

$ cmp -bl <(echo -n abcda) <(echo -n aqcde)
  2 142 b    161 q
  5 141 a    145 e

Wenn man auf Position 2 sagt, tritt ab für die erste, aber aq für die zweite auf. An Position 5 passiert ein weiterer Unterschied. Ersetzen Sie diese Zeichenfolgen einfach durch Variablen, und Sie sind fertig.

Johannes Schaub - litb
quelle
Dies funktioniert nur, wenn die Zeichenfolgen gleich lang sind!
Strpeter
11

Angenommen, Sie haben drei Zeichenfolgen

a="this is a line"
b="this is"
c="a line"

So entfernen Sie das Präfix b aus a

echo ${a#"$b"}  # a line

So entfernen Sie das Suffix c aus a

echo ${a%"$c"}  # this is
Pithikos
quelle
2
Ich denke, das ist die Bash-Methode. Es hat gut funktioniert. Diese Syntax ist allerdings etwas schwer zu verstehen.
Mikael Roos
@MikaelRoos Einverstanden. Einfacher zu lesen (für mich jedenfalls) wäre die Verwendung von sed: echo "$a" | sed "s!^$b!!g" (Ich habe das Standard-sed-Trennzeichen / für! Ausgetauscht, falls die behandelten Variablen Pfade sind. Sie können auch eine Zeichenfolge hier anstelle von echo verwenden : sed ... <<< $a.)
ACK_stoverflow
0

Ein anderes Beispiel:

before="184613 102050 83756 63054"
after="184613 102050 84192 83756 63054"

comm -23 <(tr ' ' $'\n' <<< $after | sort) <(tr ' ' $'\n' <<< $before | sort)

Ausgänge

84192

Ursprüngliche Antwort hier

Sida Zhou
quelle