Schnittpunkt zweier Listen in Bash

162

Ich versuche, ein einfaches Skript zu schreiben, das den Inhalt in zwei Listen auflistet. Verwenden wir zur Vereinfachung ls als Beispiel. Stellen Sie sich vor, "eins" und "zwei" sind Verzeichnisse.

eins = "ls eins"
zwei = "ls zwei"
Kreuzung $ eins $ zwei

Ich bin immer noch ziemlich grün in Bash, also zögern Sie nicht zu korrigieren, wie ich das mache. Ich brauche nur einen Befehl, der alle Dateien in "eins" und "zwei" druckt. Sie müssen in beiden existieren. Sie könnten dies den "Schnittpunkt" zwischen "eins" und "zwei" nennen.

Benutzer1
quelle
Hier beantwortet eigentlich nichts die Frage: Wie werden zwei Variablen in einem Bash-Skript geschnitten ?
Jameshfisher
Scheint meiner Meinung nach eine neue Frage zu sein, diese Frage wird hier klar beantwortet.
Jean-Christophe Meillaud
Ein wohl nützlicherer Ansatz ist der nahezu doppelte stackoverflow.com/questions/2312762/…
Tripleee

Antworten:

284
comm -12  <(ls 1) <(ls 2)
Ghostdog74
quelle
37
Ich kann nicht glauben, dass ich commbis heute nichts davon wusste. Das hat gerade meine ganze Woche gemacht :)
Darragh Enright
22
commerfordert, dass die Eingaben sortiert werden. In diesem Fall wird lsdie Ausgabe automatisch sortiert, andere Verwendungszwecke müssen dies jedoch möglicherweise tun:comm -12 <(some-command | sort) <(some-other-command | sort)
Alexander Bird,
11
VERWENDEN SIE die Ausgabe von ls NICHT für irgendetwas. ls ist ein Tool zum interaktiven Anzeigen von Verzeichnismetadaten. Alle Versuche, die Ausgabe von ls mit Code zu analysieren, werden abgebrochen. Globs sind viel einfacher UND korrekter: '' für Datei in * .txt ''. Lesen Sie mywiki.wooledge.org/ParsingLs
Rany Albeg Wein
2
Ich habe dies nur verwendet, um die Verwendung einer publicMethode zu finden , error()die von einem Merkmal in Kombination mit bereitgestellt wird git grep, und es war fantastisch! Ich rannte $ comm -12 <(git grep -il "\$this->error(" -- "*.php") <(git grep -il "Dash_Api_Json_Response" -- "*.php")und zum Glück hatte ich nur den Namen der Datei, die das Merkmal enthielt.
localheinz
3
Das ist urkomisch. Ich habe versucht, ein paar verrückte Sachen mit awk zu machen.
Rolf
54

Lösung mit comm

commist toll, muss aber mit sortierter Liste arbeiten. Und zum Glück verwenden wir hier lswelche von lsBash Manpage

Sortieren Sie Einträge alphabetisch, wenn weder -cftuSUX noch --sort vorhanden sind.

comm -12  <(ls one) <(ls two)

Alternative mit sort

Schnittpunkt zweier Listen:

sort <(ls one) <(ls two) | uniq -d

symmetrischer Unterschied zweier Listen:

sort <(ls one) <(ls two) | uniq -u

Bonus

Spiel damit ;)

cd $(mktemp -d) && mkdir {one,two} && touch {one,two}/file_{1,2}{0..9} && touch two/file_3{0..9}
Jean-Christophe Meillaud
quelle
2
Anstelle von Komplement denke ich, dass dies normalerweise als symmetrische Differenz bezeichnet wird .
Andrew Lazarus
29

Verwenden Sie den commBefehl:

ls one | sort > /tmp/one_list
ls two | sort > /tmp/two_list
comm -12 /tmp/one_list /tmp/two_list

"sort" wird nicht wirklich benötigt, aber ich füge es immer ein, bevor ich "comm" für alle Fälle verwende.

DVK
quelle
5
Es ist gut, es aufzunehmen, da es sortiert werden muss und er nur ls als Beispiel verwendet hat.
Thor84no
3

Eine weniger effiziente (als Kommunikations-) Alternative:

cat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -d
Benubird
quelle
1
Wenn Sie Debians / bin / dash oder eine andere Nicht-Bash-Shell in Ihren Skripten verwenden, können Sie die Ausgabe von Befehlen in Klammern verketten : (ls 1; ls 2) | sort -u | uniq -d.
Stickstoff
1
@ MikaëlMayer Du solltest den Namen der Person kennzeichnen, auf die du antwortest, sonst wird angenommen, dass du mich meinst.
Benubird
@nitrogen MikaëlMayer ist korrekt - Chainging sort -u | uniq -dbewirkt nichts, da die Sortierung die Duplikate entfernt hat, bevor uniq nach ihnen sucht. Ich denke, Sie haben nicht verstanden, was mein Befehl tut.
Benubird
@Benubird Ich konnte Ihren Befehl auch nicht dazu bringen cat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -d, etwas auszugeben. Mein Befehl sollte (ls 1; ls 2) | sort | uniq -dohne das lauten , -uum den Listenschnittpunkt anzuzeigen. @ MikaëlMayer hatte recht, dass mein ursprünglicher Befehl gebrochen war.
Stickstoff
@nitrogen Der Grund, warum ich cat verwende, ist, dass ich möchte, dass dies eine verallgemeinerbare Lösung ist, damit Sie sie durch lsetwas anderes ersetzen können, z find. Ihre Lösung lässt dies nicht zu, da einer der Befehle, wenn er zwei Zeilen gleich zurückgibt, ihn als Duplikat aufnimmt. Meins funktioniert auch dann, wenn der Benutzer ls 1/*alle Dateien in Unterverzeichnissen vergleichen möchte . Ansonsten funktioniert es ja auch. Es ist möglich, dass meine Bash-spezifisch ist.
Benubird
2

Join ist eine weitere gute Option, abhängig von der Eingabe und der gewünschten Ausgabe

join -j1 -a1 <(ls 1) <(ls 2)
frogstarr78
quelle
-1

Es gibt eine weitere Stackoverflow-Frage "Array-Schnittpunkt in Bash", die als Duplikat davon markiert ist. Meiner Meinung nach ist es nicht ganz dasselbe, da es sich bei dieser Frage um den Vergleich zweier Bash-Arrays handelt, während sich diese Frage auf Bash-Dateien konzentriert. Eine einzeilige Antwort auf die andere Frage, die jetzt geschlossen ist, lautet wie folgt:

# List1=( 0 1 2 3 4   6 7 8 9 10 11 12)
# List2=(   1 2 3   5 6   8 9    11 )
# List3=($(comm -12 <(echo ${List1[*]}| tr " " "\n"| sort) <(echo ${List2[*]} | tr " " "\n"| sort)| sort -g))
# echo ${List3[*]}
1 2 3 6 8 9 11

Das Dienstprogramm comm führt eine alphanumerische Sortierung durch, während die Antworten "Array-Schnittpunkt in Bash" Zahlen verwenden. daher die Verwendung von "sort" und "sort -g".

Chuck Newman
quelle