Persimpangan dua daftar di Bash

162

Saya mencoba menulis skrip sederhana yang akan mencantumkan konten yang ditemukan dalam dua daftar. Untuk mempermudah, mari kita gunakan ls sebagai contoh. Bayangkan "satu" dan "dua" adalah direktori.

one = `ls one`
two = `ls two`
persimpangan $ satu $ dua

Saya masih cukup hijau di bash, jadi jangan ragu untuk memperbaiki bagaimana saya melakukan ini. Saya hanya perlu beberapa perintah yang akan mencetak semua file dalam "satu" dan "dua". Mereka harus ada di keduanya. Anda mungkin menyebutnya "persimpangan" antara "satu" dan "dua".

Pengguna1
sumber
Tidak ada yang benar-benar menjawab pertanyaan: bagaimana memotong dua variabel dalam skrip Bash.
jameshfisher
Sepertinya pertanyaan baru menurut saya, pertanyaan itu jelas dijawab di sini.
Jean-Christophe Meillaud
Sebuah pendekatan yang bisa dibilang lebih bermanfaat adalah di dekat-duplikat stackoverflow.com/questions/2312762/...
tripleee

Jawaban:

284
comm -12  <(ls 1) <(ls 2)
ghostdog74
sumber
37
Tidak percaya saya tidak memiliki pengetahuan commsampai hari ini. Ini hanya membuat saya sepanjang minggu :)
Darragh Enright
22
commmembutuhkan input yang akan diurutkan. Dalam hal ini, lssecara otomatis mengurutkan outputnya, tetapi kegunaan lain mungkin perlu melakukan ini:comm -12 <(some-command | sort) <(some-other-command | sort)
Alexander Bird
11
JANGAN GUNAKAN output ls 'untuk apa pun. ls adalah alat untuk melihat secara interaktif metadata direktori. Setiap usaha untuk mem-parsing output ls 'dengan kode rusak. Gumpalan jauh lebih sederhana DAN benar: '' untuk file di * .txt ''. Baca mywiki.wooledge.org/ParsingLs
Rany Albeg Wein
2
Saya hanya menggunakan ini dalam upaya untuk menemukan penggunaan publicmetode yang error()disediakan oleh suatu sifat, dikombinasikan dengan git grep, dan itu luar biasa! Saya berlari $ comm -12 <(git grep -il "\$this->error(" -- "*.php") <(git grep -il "Dash_Api_Json_Response" -- "*.php"), dan untungnya saya berakhir dengan nama file hanya yang mengandung sifat tersebut.
localheinz
3
Ini sangat lucu. Saya mencoba melakukan beberapa hal gila dengan awk.
Rolf
54

Solusi dengan comm

commbagus tapi memang perlu bekerja dengan daftar yang diurutkan. Dan untungnya di sini kita gunakan lsyang dari lshalaman Bash man

Sortir entri menurut abjad jika tidak ada -cftuSUX atau --sort.

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

Alternatif dengan sort

Persimpangan dua daftar:

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

perbedaan simetris dari dua daftar:

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

Bonus

Main dengannya;)

cd $(mktemp -d) && mkdir {one,two} && touch {one,two}/file_{1,2}{0..9} && touch two/file_3{0..9}
Jean-Christophe Meillaud
sumber
2
Alih-alih melengkapi , saya pikir itulah yang biasanya disebut perbedaan simetris .
Andrew Lazarus
29

Gunakan commperintah:

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

"sort" tidak benar-benar dibutuhkan tetapi saya selalu memasukkannya sebelum menggunakan "comm" untuk berjaga-jaga.

DVK
sumber
5
Baik untuk memasukkannya karena memang perlu disortir, dan ia hanya menggunakan ls sebagai contoh.
Thor84no
3

Alternatif yang kurang efisien (daripada komunikasi):

cat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -d
Benubird
sumber
1
Jika Anda menggunakan Debian / bin / dasbor atau non-Bash shell di script Anda, Anda dapat keluaran rantai perintah menggunakan tanda kurung: (ls 1; ls 2) | sort -u | uniq -d.
nitrogen
1
@ MikaëlMayer Anda harus menandai nama orang yang Anda balas, jika tidak Anda anggap Anda maksud saya.
Benubird
@nitrogen MikaëlMayer benar - chainging sort -u | uniq -dtidak melakukan apa-apa, karena pengurutan telah menghapus duplikat sebelum uniq mulai mencarinya. Saya pikir Anda belum mengerti apa yang sedang saya lakukan.
Benubird
@ Benubird, aku juga tidak bisa mendapatkan perintah cat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -duntuk mengeluarkan apa pun. Perintah saya harus membaca (ls 1; ls 2) | sort | uniq -d, tanpa -u, untuk menunjukkan persimpangan daftar. @ MikaëlMayer benar bahwa perintah awal saya rusak.
nitrogen
@nitrogen Alasan mengapa saya menggunakan cat, adalah karena saya ingin ini menjadi solusi yang dapat digeneralisasikan, sehingga Anda dapat mengganti lsdengan yang lain, misalnya find. Solusi Anda tidak mengizinkan ini, karena jika salah satu dari perintah mengembalikan dua baris yang sama, itu mengambilnya sebagai duplikat. Milik saya berfungsi bahkan jika pengguna ingin melakukan ls 1/*dan membandingkan semua file di seluruh subdirektori. Kalau tidak, ya, itu berfungsi juga. Mungkin milikku khusus untuk bash.
Benubird
2

Bergabung adalah pilihan lain yang baik tergantung pada input dan output yang diinginkan

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

Ada pertanyaan Stackoverflow lain "persimpangan Array di bash," yang ditandai sebagai duplikat dari ini. Ini tidak persis sama, menurut pendapat saya, karena pertanyaan itu berbicara tentang membandingkan dua array bash, sementara pertanyaan ini berfokus pada file bash. Jawaban satu baris untuk pertanyaan lain, yang sekarang ditutup, adalah sebagai berikut:

# 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

Utilitas comm melakukan semacam alfanumerik, sedangkan "Array intersection in bash" menjawab menggunakan angka; karenanya penggunaan "sort" dan "sort -g".

Chuck Newman
sumber