Bash脚本和大文件(错误):使用重定向内置的读取内容输入会产生意外结果

jfg956

我对大文件和文件有一个奇怪的问题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

给出结果如下(我把xes用来保持数据机密,其他字符是真实的)。请注意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。受影响的版本是bashDebian 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变量用于保存的返回值lseekAslseek返回从文件开头开始的偏移量,当偏移量超过2GB时,该int值为负,这会导致测试if (r >= 0)在应该成功的地方失败。

吉勒斯“别再邪恶了”

您已经在bash中发现了一种错误。这是带有已知修复程序的已知错误。

程序将文件中的偏移量表示为具有有限大小的某些整数类型的变量。在过去,每个人几乎都使用int了所有东西,并且int类型被限制为32位(包括符号位),因此它可以存储从-2147483648到2147483647的值。如今,针对不同的事物有不同的类型名称,包括off_t用于文件中的偏移量。

默认情况下,off_t在32位平台上为32位类型(最多2GB),在64位平台上为64位类型(最多8EB)。但是,通常使用LARGEFILE选项编译程序,该选项将类型off_t切换为64位宽,并使程序调用诸如的适当功能lseek

看来您在32位平台上运行bash,并且bash二进制文件未使用大文件支持进行编译。现在,当您从常规文件中读取一行时,bash使用内部缓冲区批量读取字符以提高性能(有关更多详细信息,请参阅中的源代码builtins/read.def)。该行结束后,bash调用lseek以将文件偏移量倒回到该行的末尾位置,以防其他程序关心该文件中的位置。对的调用lseek发生在中的zsyncfc函数中lib/sh/zread.c

我没有详细阅读源代码,但是我猜想,当绝对偏移为负数时,过渡点不会顺利进行。因此,bash在超过2GB标记后重新填充缓冲区时,最终会读取错误的偏移量。

如果我的结论是错误的,并且您的bash实际上是在64位平台上运行或使用大文件支持进行编译,则肯定是一个错误。请报告给您的发行商或上游

无论如何,shell并不是处理此类大文件的正确工具。将会很慢。如果可能,请使用sed,否则请awk。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

使用goroutine进行迭代会产生意外结果

使用Repast Simphony产生意外结果

R,使用dplyr :: mutate和ifelse包含grepl()会产生意外结果

MySQL-使用LEFT JOIN会产生意外结果

Z3使用PDR引擎产生意外结果

闭包和让关键字产生意外结果

从单个文件读取并写入2个单独的文件会产生意外结果

Numpy切片多维错误产生意外结果

使用std :: set的.begin()和.end()函数会产生意外的结果

Passport和Auth :: logout()产生意外错误

使用python进行二进制读取会产生意外结果

asfreq使用Period dtype产生意外结果

使用ProcessPoolExcecutor进行多处理会产生意外结果

从文件读取数组时的ConvertTo-Json产生意外结果

使用bash重定向和输入变量编写脚本

从多个文件读取数据并将数据写入新文件会产生意外结果?

在WHERE子句中混合使用AND和OR会产生意外结果

C FILE读取产生意外错误

尝试在别名中使用`pwd`会产生意外结果

按组使用data.table的shift()产生意外结果(错误?)

replace()产生意外结果

在bash中重定向内存泄漏错误消息

Bash 文件重定向错误?

在 bash 脚本中重定向标准输入和标准输出

使用 "%" 操作会产生意外结果

手动添加到 HTML 范围输入会产生意外结果

在 Unity C# 中一次使用多个关键输入会产生意外行为

重定向内容而不更改 htaccess 文件中的 url

Shell 脚本 - IFS 读取语法错误:意外重定向