Karena semua file input sudah diurutkan, kami dapat melewati langkah penyortiran yang sebenarnya dan hanya digunakan sort -m
untuk menggabungkan file-file tersebut.
Pada beberapa sistem Unix (setahu saya hanya Linux), mungkin cukup untuk dilakukan
sort -m *.words | uniq -d >dupes.txt
untuk mendapatkan garis duplikat yang ditulis ke file dupes.txt
.
Untuk menemukan file apa yang berasal dari baris ini, Anda dapat melakukannya
grep -Fx -f dupes.txt *.words
Ini akan menginstruksikan grep
untuk memperlakukan garis dalam dupes.txt
( -f dupes.txt
) sebagai pola string tetap ( -F
). grep
juga akan mengharuskan seluruh baris cocok dengan sempurna dari awal hingga selesai ( -x
). Ini akan mencetak nama file dan baris ke terminal.
Non-Linux Unices (atau bahkan lebih banyak file)
Pada beberapa sistem Unix, 30000 nama file akan diperluas ke string yang terlalu panjang untuk dilewatkan ke satu utilitas (artinya sort -m *.words
akan gagal Argument list too long
, yang terjadi pada sistem OpenBSD saya). Bahkan Linux akan mengeluh tentang ini jika jumlah file jauh lebih besar.
Menemukan korban penipuan
Ini berarti bahwa dalam kasus umum (ini juga akan bekerja dengan banyak lebih dari hanya 30.000 file), seseorang harus "chunk" penyortiran:
rm -f tmpfile
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh
Atau, buat tmpfile
tanpa xargs
:
rm -f tmpfile
find . -type f -name '*.words' -exec sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh {} +
Ini akan menemukan semua file di direktori saat ini (atau di bawah) yang namanya cocok *.words
. Untuk sepotong nama-nama ini berukuran tepat, ukuran yang ditentukan oleh xargs
/ find
, itu menggabungkan mereka bersama-sama ke dalam tmpfile
file yang diurutkan . Jika tmpfile
sudah ada (untuk semua kecuali chunk pertama), file ini juga digabungkan dengan file lain di chunk saat ini. Bergantung pada panjang nama file Anda, dan panjang maksimum yang diperbolehkan dari sebuah baris perintah, ini mungkin memerlukan lebih dari 10 kali jalan skrip internal ( find
/ xargs
akan melakukannya secara otomatis).
sh
Skrip "internal" ,
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi
gunakan sort -o tmpfile
untuk menghasilkan tmpfile
(ini tidak akan menimpa tmpfile
bahkan jika ini juga merupakan input untuk sort
) dan -m
untuk melakukan penggabungan. Di kedua cabang, "$@"
akan diperluas ke daftar nama file yang dikutip secara individual yang diteruskan ke skrip dari find
atau xargs
.
Kemudian, jalankan uniq -d
pada tmpfile
untuk mendapatkan semua baris yang diduplikasi:
uniq -d tmpfile >dupes.txt
Jika Anda menyukai prinsip "KERING" ("Don't Repeat Yourself"), Anda dapat menulis skrip internal sebagai
if [ -f tmpfile ]; then
t=tmpfile
else
t=/dev/null
fi
sort -o tmpfile -m "$t" "$@"
atau
t=tmpfile
[ ! -f "$t" ] && t=/dev/null
sort -o tmpfile -m "$t" "$@"
Dari mana mereka berasal?
Untuk alasan yang sama seperti di atas, kami tidak dapat menggunakan grep -Fx -f dupes.txt *.words
untuk menemukan dari mana duplikasi ini berasal, jadi alih-alih kami gunakan find
lagi:
find . -type f -name '*.words' \
-exec grep -Fx -f dupes.txt {} +
Karena tidak ada pemrosesan "rumit" yang harus dilakukan, kami dapat meminta grep
langsung dari -exec
. The -exec
pilihan mengambil perintah utilitas dan akan menempatkan nama-nama yang ditemukan dalam {}
. Dengan +
di bagian akhir, find
akan menempatkan argumen {}
sebanyak menggantikan shell saat ini mendukung dalam setiap doa utilitas.
Agar benar - benar benar, orang mungkin ingin menggunakan keduanya
find . -type f -name '*.words' \
-exec grep -H -Fx -f dupes.txt {} +
atau
find . -type f -name '*.words' \
-exec grep -Fx -f dupes.txt /dev/null {} +
untuk memastikan bahwa nama file selalu termasuk dalam keluaran dari grep
.
Variasi pertama digunakan grep -H
untuk selalu menampilkan nama file yang cocok. Variasi terakhir menggunakan fakta yang grep
akan menyertakan nama file yang cocok jika lebih dari satu file diberikan pada baris perintah.
Ini penting karena potongan terakhir dari nama file yang dikirim grep
dari find
mungkin sebenarnya hanya berisi nama file tunggal, dalam hal grep
ini tidak akan menyebutkannya dalam hasilnya.
Materi bonus:
Membedah perintah find
+ xargs
+ sh
:
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh
find . -type f -name '*.words'
hanya akan menghasilkan daftar nama path dari direktori saat ini (atau di bawah) di mana setiap nama path adalah dari file biasa ( -type f
) dan yang memiliki komponen nama file di akhir yang cocok *.words
. Jika hanya direktori saat ini yang akan dicari, seseorang dapat menambahkan -maxdepth 1
setelah .
, sebelumnya -type f
.
-print0
akan memastikan bahwa semua nama path yang ditemukan dikeluarkan dengan karakter \0
( nul
) sebagai pembatas. Ini adalah karakter yang tidak valid di jalur Unix dan memungkinkan kita untuk memproses nama path meskipun mengandung karakter baris baru (atau hal-hal aneh lainnya).
find
pipa hasilnya ke xargs
.
xargs -0
akan membaca \0
daftar nama path yang telah direvisi dan akan mengeksekusi utilitas yang diberikan berulang kali dengan potongan-potongan ini, memastikan bahwa utilitas dieksekusi dengan argumen yang cukup untuk tidak menyebabkan shell mengeluh tentang daftar argumen yang terlalu panjang, sampai tidak ada input lagi dari find
.
Utilitas yang dipanggil oleh xargs
adalah sh
dengan skrip yang diberikan pada baris perintah sebagai string menggunakan -c
benderanya.
Ketika menggunakan sh -c '...some script...'
argumen berikut, argumen akan tersedia untuk skrip $@
, kecuali argumen pertama , yang akan ditempatkan di $0
(ini adalah "nama perintah" yang dapat Anda temukan misalnya top
jika Anda cukup cepat). Inilah sebabnya kami menyisipkan string sh
sebagai argumen pertama setelah akhir skrip aktual. String sh
adalah argumen dummy dan bisa berupa kata tunggal (beberapa tampaknya lebih suka _
atau sh-find
).
fi' sh
?