Ganti teks dengan cepat dalam file yang sangat besar

25

Saya memiliki file teks 25GB yang perlu diganti string hanya pada beberapa baris. Saya dapat menggunakan dengan sedsukses tetapi butuh waktu sangat lama untuk dijalankan.

sed -i 's|old text|new text|g' gigantic_file.sql

Apakah ada cara yang lebih cepat untuk melakukan ini?

Eisaacson
sumber
Apakah Anda tahu nomor baris tempat teks akan diganti? Jika bukan satu-satunya pilihan Anda untuk mempercepatnya adalah mendapatkan komputer yang lebih cepat. Fakta bahwa Anda memiliki sejumlah besar data berarti akan membutuhkan banyak waktu untuk mencari data.
David King
Saya bisa mendapatkan nomor baris dengan cepat, jadi ya.
eisaacson
Anda juga dapat menggunakan beberapa inti CPU untuk mempercepatnya - rankfocus.com/use-cpu-cores-linux-commands
ahaswer
Jangan gunakan sed untuk file besar. Lihatlah vi atau vim sebagai gantinya.
MikeJRamsey56

Jawaban:

26

Anda dapat mencoba:

sed -i '/old text/ s//new text/g' gigantic_file.sql

Dari referensi ini :

MENGOPTIMALKAN KECEPATAN: Jika kecepatan eksekusi perlu ditingkatkan (karena file input besar atau prosesor lambat atau hard disk), substitusi akan dieksekusi lebih cepat jika ekspresi "find" ditentukan sebelum memberikan "s /.../. ../" petunjuk.

Berikut ini perbandingan file 10G. Sebelum:

$ time sed -i 's/original/ketan/g' wiki10gb
real    5m14.823s
user    1m42.732s
sys     1m51.123s

Setelah:

$ time sed -i '/ketan/ s//original/g' wiki10gb
real    4m33.141s
user    1m20.940s
sys     1m44.451s
mkc
sumber
Yang terakhir sedsalah eja. Saya mengedit posting ini kemarin untuk memperbaiki sedperintah terakhir yang seharusnya time sed -i '/original/ s//ketan/g' wiki10gbdan tidak time sed -i '/ketan/ s//original/g' wiki10gb. Saya mengembalikan hasil edit saya hari ini karena 1. kali tidak lagi cocok dengan perintah dan 2. Saya telah melakukan tes yang sama dengan GNU pada file 3+ GB dan saya tidak melihat perbedaan antara dua sedalternatif. Saya menduga bahwa perbedaan waktu adalah karena kesalahan ejaan.
xhienne
@xhienne Saya tidak yakin apa yang Anda maksud dengan salah mengeja. Dalam menjalankan pertama, saya mengganti kata 'asli' dengan 'ketan' dan yang kedua saya mengganti istilah 'ketan' dengan istilah 'asli' menghasilkan jumlah substitusi yang sama dalam kedua kasus.
mkc
1
Saya menerapkan "perbaikan" yang dilaporkan oleh pengguna baru dengan reputasi tidak cukup. Sekarang saya mengerti apa yang Anda lakukan. Namun, jika Anda ingin membuktikan bahwa satu sintaks lebih baik daripada yang lain, Anda harus melakukan operasi yang sama persis yang tidak terjadi di sini (berdasarkan CPU, mencari string 5-char tidak sama dengan mencari String 7-char). Selain itu, tes semacam ini pada file 10GB sangat tergantung pada beban mesin Anda (CPU, disk). Saya melihat banyak fluktuasi dalam timehasil secara pribadi, tetapi secara keseluruhan, tidak ada perbedaan waktu.
xhienne
Saya percaya ini terkait - lihat jawaban yang diterima di sini, stackoverflow.com/questions/11145270/... >> sed stream seluruh file, tetapi sebagaimana disebutkan dalam jawaban ini, menentukan nomor baris (jika diketahui) membantu: dalam kasus saya , peningkatan kecepatan eksekusi ~ 2 kali lipat (GNU sed 4.5). Anda dapat grep -n atau ripgrep (rg) untuk menemukan nomor baris, berdasarkan pencarian pola. Akibatnya, menentukan nomor baris seperti memiliki hasil pencarian pada file itu, sesuai jawaban di atas.
Victoria Stuart
1

Jawaban singkatnya adalah "Tidak" - faktor pembatas Anda pada operasi semacam ini adalah disk IO. Tidak ada cara untuk streaming 25GB disk lebih cepat. Anda mungkin mendapatkan sedikit peningkatan jika Anda tidak mengedit di tempat, dan Anda menulis hasil dari sedke drive yang terpisah (jika Anda memiliki satu tersedia) - karena dengan cara itu Anda dapat membaca dari satu, sementara menulis ke yang lain dan ada sedikit lebih sedikit pertengkaran sebagai hasilnya.

Anda mungkin dapat mempercepatnya sedikit dengan tidak menggunakan mesin regex untuk setiap baris - jadi misalnya menggunakan perl (Saya cukup yakin Anda bisa melakukan ini dengan sedtetapi saya tidak tahu sintaks) - ini akan mulai dari baris 10.000 dan seterusnya.

perl -pe '$. > 10_000 && s/old_text/new_text/g' 

Dan jika ada semacam komplikasi dalam RE (metacharacters) maka meminimalkan itu akan sedikit meningkatkan efisiensi mesin regex.

Sobrique
sumber
1
sed -i '10000,$ s/old_text/new_text/g'
Sed
Menyenangkan. Saya tidak tahu bagaimana sedmembandingkan - saya menganggap sedikit lebih cepat, tetapi tidak banyak karena ukuran file.
Sobrique
Saya berasumsi perl lebih cepat daripada sed, tetapi sed agak kurang samar, atau lebih tepatnya membutuhkan lebih sedikit kurva pembelajaran awal.
Dani_l
1
Lihat, sekarang aku akan mengatakan sebaliknya - Anda dapat (hampir) menulis seddi perl, tetapi yang terakhir juga memungkinkan Anda menulis lebih verbose script juga.
Sobrique
0

Jika teks baru dan lama memiliki panjang yang sama, Anda dapat mencari ke dalam file dan hanya menulis byte yang diubah, alih-alih menyalin seluruh file. Kalau tidak, Anda terjebak dalam memindahkan banyak data.

Catatan: ini rumit dan melibatkan penulisan kode khusus.

Lihat halaman manual untuk fseek jika Anda bekerja di C atau C ++, atau pembungkus bahasa yang Anda sukai untuk mencari dan menulis panggilan sistem.

Jika Anda bersikeras menggunakan baris perintah saja, dan Anda bisa mendapatkan byte offset teks, Anda dapat menulis teks pengganti di tempat dengan perintah "dd" yang ditulis dengan hati-hati.

dicuri moment
sumber