Bash - sparuj każdą linię pliku

10

To pytanie jest ściśle związane z tym i tym pytaniem. Mam plik, który zawiera kilka linii, z których każda jest ścieżką do pliku. Teraz chcę sparować każdą linię z każdą inną linią (nie samą). Również para A Bjest równa B Aparze dla moich celów, więc należy utworzyć tylko jedną z tych kombinacji.

Przykład

files.dat czyta się tak w notacji skrótowej, każda litera jest ścieżką do pliku (bezwzględną lub względną)

a
b
c
d
e

Zatem mój wynik powinien wyglądać mniej więcej tak:

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

Najlepiej chciałbym rozwiązać to w bash. W przeciwieństwie do innych pytań, moja lista plików jest raczej niewielka (około 200 linii), więc używanie pętli i pojemności pamięci RAM nie stanowi problemu.

Enno
źródło
Czy musi to być poprawne bash , czy po prostu coś dostępnego w linii poleceń bash? Inne narzędzia są lepiej przygotowane do przetwarzania tekstu.
Jeff Schaller
@JeffSchaller Coś dostępnego z linii poleceń bash. Przepraszam, byłem trochę niejasny
Enno
To prawie staje się Code Golf : P
Richard de Wit
3
Zasadniczo, tak długo, jak trzeba zrobić coś nietrywialnego, używaj swojego ulubionego języka skryptowego w BASH. Będzie mniej delikatny (na przykład w stosunku do znaków specjalnych lub spacji) i będzie łatwiejszy do rozszerzenia, gdy będziesz go potrzebować (jeśli potrzebujesz trzech lub odfiltruj niektóre z nich). Python lub Perl powinny być zainstalowane w prawie każdym Linux-ie, więc są dobrym wyborem (chyba że pracujesz na systemach wbudowanych, takich jak Busybox).
Davidmh,

Odpowiedzi:

7

Użyj tego polecenia:

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

PROCINFOmoże być gawkrozszerzeniem. Jeśli awknie obsługuje tego, po prostu pomiń PROCINFO["sorted_in"] = "@ind_str_asc"linię i potokuj wyjście sort(jeśli chcesz posortować dane wyjściowe).

( Nie wymaga to sortowania danych wejściowych).

G-Man mówi „Przywróć Monikę”
źródło
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

Zakłada się, że żadna linia w pliku wejściowym nie zawiera białych znaków. Zakłada również, że plik jest posortowany .

joinPolecenie tworzy pełny przekrój produkt z linii w pliku. Robi to, łącząc plik ze sobą w nieistniejącym polu. Niestandardowy -j 2może zostać zastąpiony przez -1 2 -2 2(ale nie przez, -j2chyba że używasz GNU join).

awkPolecenie odczytuje wynik ten i wyprowadza tylko wyniki, które są pary, które nie zostały jeszcze widoczne.

Kusalananda
źródło
Co rozumiesz przez „plik jest posortowany”? Posortowane według jakich kryteriów?
Enno,
@Enno Posortowano sposób sort -bby to posortować. joinwymagają posortowanych plików wejściowych.
Kusalananda
8

pythonRozwiązaniem. Plik wejściowy jest dostarczany itertools.combinationsze standardowej biblioteki, która generuje krotki o długości 2, które są formatowane i drukowane na standardowe wyjście.

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
źródło
6

Jeśli rubyzainstalowałeś:

$ 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 slurp cały plik (powinno być w porządku, ponieważ w OP podano, że rozmiar pliku jest mały)
  • -F'\n'podzielone na podstawie nowej linii, więc każda linia będzie elementem w $Ftablicy
  • $F.combination(2)generuj kombinacje 2elementów na raz
  • { |c| puts c.join(" ")} wydrukuj zgodnie z wymaganiami
  • jeśli plik wejściowy może zawierać duplikaty, użyj $F.uniq.combination(2)


dla 3 elementów jednocześnie:

$ 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


Z perl(nie ogólne)

$ 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


Z 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
źródło
5

Oto jeden w czystej skorupie.

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

Przykład:

~ (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
źródło
1
Zastępowanie poleceń <file.dat xargs test.shtest.sh $(cat file.dat)
kończy
1

Za pomocą Perlmożemy to zrobić jak pokazano:

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