Bash - Koppeln Sie jede Dateizeile

10

Diese Frage hängt stark mit dieser und dieser Frage zusammen. Ich habe eine Datei, die mehrere Zeilen enthält, wobei jede Zeile ein Pfad zu einer Datei ist. Jetzt möchte ich jede Zeile mit jeder anderen Zeile (nicht selbst) koppeln. Auch ein Paar A Bist B Afür meine Zwecke gleich einem Paar, daher sollte nur eine dieser Kombinationen hergestellt werden.

Beispiel

files.dat liest sich so in einer Kurzschreibweise, jeder Buchstabe ist ein Dateipfad (absolut oder relativ)

a
b
c
d
e

Dann sollte mein Ergebnis ungefähr so ​​aussehen:

a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

Am liebsten möchte ich das in bash lösen. Im Gegensatz zu den anderen Fragen ist meine Dateiliste eher klein (ca. 200 Zeilen), sodass die Verwendung von Schleifen und RAM-Kapazität keine Probleme darstellt.

Enno
quelle
Muss es in Bash sein oder nur etwas, das über die Bash-Befehlszeile verfügbar ist? Andere Dienstprogramme sind besser positioniert, um Text zu verarbeiten.
Jeff Schaller
@ JeffSchaller Etwas, auf das über die Bash-Befehlszeile zugegriffen werden kann. Ich war ein bisschen unklar, sorry
Enno
Dies wird fast zu einem Code Golf : P
Richard de Wit
3
Verwenden Sie in der Regel Ihre bevorzugte Skriptsprache über BASH, solange Sie etwas nicht Triviales tun müssen. Es ist weniger zerbrechlich (z. B. gegen Sonderzeichen oder Leerzeichen) und lässt sich viel einfacher erweitern, wenn Sie es benötigen (wenn Sie drei benötigen oder einige davon herausfiltern). Python oder Perl sollten in fast jeder Linux-Box installiert sein, daher sind sie eine gute Wahl (es sei denn, Sie arbeiten an eingebetteten Systemen wie Busybox).
Davidmh

Antworten:

7

Verwenden Sie diesen Befehl:

awk '{ name[$1]++ }
    END { PROCINFO["sorted_in"] = "@ind_str_asc"
        for (v1 in name) for (v2 in name) if (v1 < v2) print v1, v2 }
        ' files.dat

PROCINFOkann eine gawkErweiterung sein. Wenn Sie awkdies nicht unterstützen, lassen Sie einfach die PROCINFO["sorted_in"] = "@ind_str_asc"Zeile weg und leiten Sie die Ausgabe weiter sort(wenn Sie die Ausgabe sortieren möchten).

(Dies gilt nicht erfordert die Eingabe sortiert werden.)

G-Man sagt "Reinstate Monica"
quelle
8
$ join -j 2 -o 1.1,2.1 file file | awk '!seen[$1,$2]++ && !seen[$2,$1]++'
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

Dies setzt voraus, dass keine Zeile in der Eingabedatei Leerzeichen enthält. Es wird auch davon ausgegangen, dass die Datei sortiert ist .

Der joinBefehl erstellt das vollständige Kreuzprodukt der Zeilen in der Datei. Dazu wird die Datei in einem nicht vorhandenen Feld mit sich selbst verbunden. Der Nicht-Standard -j 2kann durch ersetzt werden -1 2 -2 2(jedoch nicht durch, es -j2sei denn, Sie verwenden GNU join).

Der awkBefehl liest das Ergebnis und gibt nur Ergebnisse aus, bei denen es sich um Paare handelt, die noch nicht gesehen wurden.

Kusalananda
quelle
Was meinst du mit "die Datei ist sortiert"? Nach welchen Kriterien sortiert?
Enno
@Enno Sortiert die sort -bArt und Weise würde es sortieren. joinerfordern sortierte Eingabedateien.
Kusalananda
8

Eine pythonLösung. Die Eingabedatei wird itertools.combinationsaus der Standardbibliothek eingespeist , die Tupel mit 2 Längen generiert, die formatiert und in der Standardausgabe gedruckt werden.

python3 -c 'from itertools import combinations
with open("file") as f:
    lines = (line.rstrip() for line in f)
    lines = ("{} {}".format(x, y) for x, y in combinations(lines, 2))
    print(*lines, sep="\n")
'
iruvar
quelle
6

Wenn Sie rubyinstalliert haben:

$ ruby -0777 -F'\n' -lane '$F.combination(2) { |c| puts c.join(" ")}' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
  • -0777 Schlürfen Sie die gesamte Datei (sollte in Ordnung sein, da in OP erwähnt wird, dass die Dateigröße klein ist).
  • -F'\n'Auf der Grundlage der neuen Zeile wird aufgeteilt, sodass jede Zeile ein Element im $FArray ist
  • $F.combination(2)Generieren Sie Kombinationselemente 2gleichzeitig
  • { |c| puts c.join(" ")} nach Bedarf drucken
  • Wenn die Eingabedatei Duplikate enthalten kann, verwenden Sie $F.uniq.combination(2)


für 3 Elemente gleichzeitig:

$ ruby -0777 -F'\n' -lane '$F.combination(3) { |c| puts c.join(" ")}' ip.txt
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e


Mit perl(nicht generisch)

$ perl -0777 -F'\n' -lane 'for $i (0..$#F) {
                             for $j ($i+1..$#F) { 
                               print "$F[$i] $F[$j]\n" } }' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e


Mit awk

$ awk '{ a[NR]=$0 }
       END{ for(i=1;i<=NR;i++)
              for(j=i+1;j<=NR;j++)
                print a[i], a[j] }' ip.txt 
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
Sundeep
quelle
5

Hier ist einer in reiner Schale.

test $# -gt 1 || exit
a=$1
shift
for f in "$@"
do
  echo $a $f
done
exec /bin/sh $0 "$@"

Beispiel:

~ (137) $ sh test.sh $(cat file.dat)
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
~ (138) $ 
EdC
quelle
1
Kommandosubstitution Streifen Hinterzeilenumbrüche, so dass Sie besser sind weg mit so etwas wie <file.dat xargs test.shalstest.sh $(cat file.dat)
Iruvar
1

Mit Perlkönnen wir es wie gezeigt machen:

$ perl -lne '
     push @A, $_}{
     while ( @A ) {
        my $e = shift @A;
        print "$e $_" for @A;
     }
' input.txt
Rakesh Sharma
quelle