我对大文件和文件有一个奇怪的问题bash
。这是上下文:
- 我有一个大文件:75G和400,000,000+行(这是一个日志文件,我不好,我让它增长了)。
- 每行的前10个字符是格式为YYYY-MM-DD的时间戳。
- 我想分割该文件:每天一个文件。
我尝试了以下无效的脚本。 我的问题是此脚本无法运行,而不是替代解决方案。
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
调试后,我在new_file
变量中发现了问题。该脚本:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
给出结果如下(我把x
es用来保持数据机密,其他字符是真实的)。请注意dh
和较短的字符串:
...
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
...
我的文件格式没有问题。该脚本cut -c 1-10 file.log | uniq -c
仅给出有效的时间戳。有趣的是,上述输出的一部分变为cut ... | uniq -c
:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
我们可以看到,在uniq count之后4474604
,我的初始脚本失败了。
我是否达到了我不知道的bash极限,是否在bash中发现了bug(似乎不太可能缝制),或者我做错了什么?
更新:
读取文件的2G后会发生此问题。它的接缝read
和重定向不喜欢比2G大的文件。但仍在寻找更精确的解释。
Update2:
绝对看起来像个错误。可以用以下方式复制它:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
但这可以作为一种解决方法(它表明我发现对有用cat
):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
一个错误已经提交给GNU和Debian。受影响的版本是bash
Debian Squeeze 6.0.2和6.0.4上的4.1.5。
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
更新3:
感谢Andreas Schwab对我的错误报告做出了迅速反应,这是解决此不良行为的补丁。lib/sh/zread.c
正如Gilles早先指出的那样,受影响的文件:
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;
该r
变量用于保存的返回值lseek
。As lseek
返回从文件开头开始的偏移量,当偏移量超过2GB时,该int
值为负,这会导致测试if (r >= 0)
在应该成功的地方失败。
read
bash中语句限制的方向。