Bash - pasangkan setiap baris file


10

Pertanyaan ini sangat terkait dengan ini dan pertanyaan ini . Saya memiliki file yang berisi beberapa baris di mana setiap baris adalah jalur ke file. Sekarang saya ingin memasangkan setiap baris dengan setiap baris yang berbeda (bukan dirinya sendiri). Sepasang A Bjuga sama dengan B Asepasang untuk keperluan saya, jadi hanya satu dari kombinasi ini yang harus dihasilkan.

Contoh

files.dat berbunyi seperti ini dalam notasi singkat, setiap huruf adalah path file (absolut atau relatif)

a
b
c
d
e

Maka hasil saya akan terlihat seperti ini:

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

Lebih disukai saya ingin menyelesaikan ini di bash. Berbeda dengan pertanyaan lain, daftar file saya agak kecil (sekitar 200 baris), jadi menggunakan loop dan kapasitas RAM tidak menimbulkan masalah.


Apakah harus di bash proper, atau hanya sesuatu yang tersedia melalui bash commandline? Utilitas lain diposisikan lebih baik untuk memproses teks.
Jeff Schaller

@JeffSchaller Sesuatu dapat diakses melalui bash commandline. Saya agak tidak jelas, maaf
Enno

Ini hampir menjadi Golf Code : P
Richard de Wit

3
Sebagai aturan umum, selama Anda perlu melakukan sesuatu yang tidak sepele, gunakan bahasa skrip favorit Anda di atas BASH. Ini akan kurang rapuh (misalnya, terhadap karakter atau spasi khusus), dan jauh lebih mudah untuk diperluas kapan pun Anda membutuhkannya (jika Anda membutuhkan tiga, atau menyaring beberapa dari mereka). Python atau Perl harus dipasang di hampir semua kotak Linux, jadi itu adalah pilihan yang baik (kecuali jika Anda bekerja pada sistem embedded, seperti Busybox).
Davidmh

Jawaban:


7

Gunakan perintah ini:

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

PROCINFOmungkin gawkekstensi. Jika Anda awktidak mendukungnya, tinggalkan saja PROCINFO["sorted_in"] = "@ind_str_asc"saluran dan pipa output sort(jika Anda ingin output diurutkan).

(Ini tidak memerlukan input untuk diurutkan.)


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

Ini mengasumsikan bahwa tidak ada baris dalam file input berisi spasi. Itu juga mengasumsikan bahwa file diurutkan .

The joinperintah menciptakan produk silang penuh dari baris dalam file. Ini dilakukan dengan menggabungkan file dengan dirinya sendiri di bidang yang tidak ada. Non-standar -j 2dapat digantikan oleh -1 2 -2 2(tetapi tidak dengan -j2kecuali Anda menggunakan GNU join).

The awkperintah membaca hasil ini dan hanya output hasil yang pasangan yang belum terlihat.


Apa yang Anda maksud dengan "file diurutkan"? Diurutkan berdasarkan kriteria mana?
Enno

@ Enno Diurutkan cara sort -bakan mengurutkannya. joinmemerlukan file input yang diurutkan.
Kusalananda

8

Sebuah pythonsolusi. File input diumpankan ke itertools.combinationsdari perpustakaan standar, yang menghasilkan tuple 2-panjang yang diformat dan dicetak ke output standar.

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")
'

6

Jika Anda telah rubymenginstal:

$ 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 seluruh file (harus baik-baik saja seperti yang disebutkan dalam OP bahwa ukuran file kecil)
  • -F'\n'perpecahan berdasarkan baris baru, sehingga setiap baris akan menjadi elemen dalam $Farray
  • $F.combination(2)menghasilkan 2elemen kombinasi sekaligus
  • { |c| puts c.join(" ")} cetak sesuai kebutuhan
  • jika file input dapat berisi duplikat, gunakan $F.uniq.combination(2)


untuk 3 elemen sekaligus:

$ 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


Dengan perl(bukan generik)

$ 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


Dengan 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

5

Inilah satu di shell murni.

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

Contoh:

~ (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) $ 

1
Perintah substitusi strip tertinggal baris, sehingga Anda lebih baik dengan sesuatu seperti <file.dat xargs test.shdaritest.sh $(cat file.dat)
Iruvar

1

Menggunakan Perlkita bisa melakukannya seperti yang ditunjukkan:

$ perl -lne '
     push @A, $_}{
     while ( @A ) {
        my $e = shift @A;
        print "$e $_" for @A;
     }
' input.txt
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.