Saya memiliki masalah aneh dengan file besar dan bash
. Inilah konteksnya:
- Saya memiliki file besar: 75G dan 400.000.000 + baris (ini adalah file log, salah saya, saya biarkan tumbuh).
- 10 karakter pertama dari setiap baris adalah stempel waktu dalam format YYYY-MM-DD.
- Saya ingin membagi file itu: satu file per hari.
Saya mencoba dengan skrip berikut yang tidak berfungsi. Pertanyaan saya adalah tentang skrip ini tidak berfungsi, bukan solusi alternatif .
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
Setelah debugging, saya menemukan masalah dalam new_file
variabel. Skrip ini:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
memberikan hasilnya di bawah (saya meletakkan x
es untuk menjaga data rahasia, karakter lain adalah yang asli). Perhatikan dh
string dan lebih pendek:
...
27402 2011-xx-x4
27262 2011-xx-x5
22514 2011-xx-x6
17908 2011-xx-x7
...
3227382 2011-xx-x9
4474604 2011-xx-x0
1557680 2011-xx-x1
1 2011-xx-x2
3 2011-xx-x1
...
12 2011-xx-x1
1 2011-xx-dh
1 2011-xx-x1
1 208--
1 2011-xx-x1
1 2011-xx-dh
1 2011-xx-x1
...
Ini bukan masalah dalam format file saya . Script cut -c 1-10 file.log | uniq -c
hanya memberikan perangko waktu yang valid. Menariknya, bagian dari output di atas menjadi dengan cut ... | uniq -c
:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
Kita dapat melihat bahwa setelah hitungan uniq 4474604
, skrip awal saya gagal.
Apakah saya mencapai batas dalam bash yang saya tidak tahu, apakah saya menemukan bug di bash (sepertinya tidak mungkin), atau apakah saya melakukan sesuatu yang salah?
Perbarui :
Masalahnya terjadi setelah membaca 2G file. Jahitan read
dan pengalihan tidak suka file yang lebih besar dari 2G. Namun masih mencari penjelasan yang lebih tepat.
Pembaruan2 :
Ini pasti terlihat seperti bug. Itu dapat direproduksi dengan:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
tetapi ini berfungsi dengan baik sebagai solusi (sepertinya saya menemukan penggunaan yang bermanfaat cat
):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
Bug telah diajukan ke GNU dan Debian. Versi yang terpengaruh adalah bash
4.1.5 pada Debian Squeeze 6.0.2 dan 6.0.4.
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
Pembaruan3:
Terima kasih kepada Andreas Schwab yang bereaksi cepat terhadap laporan bug saya, ini adalah tambalan yang merupakan solusi untuk perilaku buruk ini. File yang terpengaruh adalah lib/sh/zread.c
seperti yang Gilles tunjukkan lebih cepat:
diff --git a/lib/sh/zread.c b/lib/sh/zread.c index 0fd1199..3731a41 100644
--- a/lib/sh/zread.c
+++ b/lib/sh/zread.c @@ -161,7 +161,7 @@ zsyncfd (fd)
int fd; { off_t off;
- int r;
+ off_t r;
off = lused - lind; r = 0;
The r
variabel digunakan untuk menyimpan nilai kembali dari lseek
. Ketika lseek
mengembalikan offset dari awal file, ketika lebih dari 2GB, int
nilainya negatif, yang menyebabkan tes if (r >= 0)
gagal di tempat yang seharusnya berhasil.
read
pernyataan dalam bash.