Saya menggunakan banyak grep awk sort di shell unix saya untuk bekerja dengan file teks kolom yang dipisahkan tab berukuran sedang (sekitar 10M-100M). Dalam hal ini shell unix adalah spreadsheet saya.
Tapi saya punya satu masalah besar, yaitu memilih catatan yang diberikan daftar ID.
Memiliki table.csv
file dengan format id\tfoo\tbar...
dan ids.csv
file dengan daftar id, hanya pilih catatan table.csv
dengan id yang ada di ids.csv
.
jenis /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids tetapi dengan shell, bukan perl.
grep -F
jelas menghasilkan positif palsu jika id lebar variabel.
join
adalah utilitas saya tidak pernah tahu. Pertama-tama, ini membutuhkan pengurutan alfabet (file saya biasanya diurutkan secara numerik), tetapi bahkan kemudian saya tidak bisa membuatnya bekerja tanpa mengeluh tentang urutan yang salah dan melewatkan beberapa catatan. Jadi saya tidak suka itu. grep -f terhadap file dengan ^id\t
-s sangat lambat ketika jumlah id besar.
awk
rumit.
Apakah ada solusi bagus untuk ini? Adakah alat khusus untuk file yang dipisahkan tab? Fungsionalitas ekstra akan sangat disambut juga.
UPD: Dikoreksi sort
->join
grep -f
terlalu lambat, mempertahankan strategi ini kedengarannya lebih banyak masalah daripada nilainya - variasi kemungkinan akan menjadi mangsa masalah kinerja O (N * M) yang sama. Mungkin waktu Anda akan lebih baik dihabiskan untuk belajar bagaimana menggunakan SQL DB yang dinormalisasi ...awk
.sort
dapat melakukan segala macam penyortiran, numerik, alfabet dan lainnya Lihatman sort
.Jawaban:
Saya kira Anda
grep -f
tidak bermaksudgrep -F
tetapi Anda sebenarnya membutuhkan kombinasi keduanya dan-w
:Alasan Anda mendapatkan false positive adalah (saya kira, Anda tidak menjelaskan) karena jika sebuah id dapat dimuat di yang lain, maka keduanya akan dicetak.
-w
menghapus masalah ini dan-F
memastikan pola Anda diperlakukan sebagai string, bukan ekspresi reguler. Dariman grep
:Jika positif palsu Anda adalah karena ID dapat hadir di bidang non-ID, gantilah file Anda:
atau, lebih cepat:
Secara pribadi, saya akan melakukan ini
perl
:sumber
^
dengan -F, Anda tidak dapat menargetkan kolom pertama secara khusus.^id\t
bit dari OP menyiratkanid
mungkin terjadi di kolom lain. Jika tidak, ini tidak masalah.The
join
utilitas adalah apa yang Anda inginkan. Itu memang membutuhkan file input untuk diurutkan secara leksikal.Dengan asumsi shell Anda adalah bash atau ksh:
Tanpa perlu menyortir, solusi awk yang biasa adalah
sumber
join
bukan kludge: kata-kata Anda itu Anda tidak bisa mengetahuinya. Buka pikiran Anda dan pelajari. Output apa yang Anda dapatkan, dan apa bedanya dengan apa yang Anda harapkan?join
.awk
solusi di sini adalah sangat cepat dan efisien untuk tujuan saya (saya mengeluarkan himpunan bagian dari beberapa ratus dari file dengan garis-garis ~ 100M)Jawaban untuk pertanyaan SO ini membantu saya menyiasati orang-orang yang canggung dengan bergabung. Pada dasarnya, ketika Anda mengurutkan file dalam persiapan untuk mengirimnya untuk bergabung, Anda perlu memastikan Anda mengurutkan berdasarkan kolom tempat Anda bergabung. Jadi jika itu yang pertama, Anda perlu memberi tahu apa karakter pemisah dalam file dan bahwa Anda ingin mengurutkannya di bidang pertama (dan hanya bidang pertama). Kalau tidak, jika bidang pertama memiliki lebar variabel (misalnya), pemisah Anda dan mungkin bidang lain mungkin mulai mempengaruhi urutan pengurutan.
Jadi, gunakan opsi -t penyortiran untuk menentukan karakter pemisah Anda, dan gunakan opsi -k untuk menentukan bidang (mengingat bahwa Anda memerlukan bidang awal dan akhir - bahkan jika itu sama - atau itu akan mengurutkan dari karakter itu ke akhir baris).
Jadi untuk file yang dipisahkan tab seperti dalam pertanyaan ini, berikut ini akan berfungsi (dengan terima kasih atas jawaban glenn untuk struktur):
join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv
(Untuk referensi, flag -d berarti semacam kamus. Anda mungkin juga ingin menggunakan flag -b untuk mengabaikan spasi putih terkemuka, lihat
man sort
danman join
).Sebagai contoh yang lebih umum, anggap Anda bergabung dengan dua file yang dipisahkan koma -
input1.csv
pada kolom ketiga daninput2.csv
keempat. Anda bisa menggunakannyajoin -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv
Di sini opsi
-1
dan-2
menentukan bidang mana yang akan digabung dalam file input pertama dan kedua masing-masing.sumber
Anda juga dapat menggunakan ruby untuk melakukan hal serupa:
sumber