从Bash脚本逐行读取2gb文件会导致“xmalloc:无法分配...”错误

时间:2015-01-20 06:20:14

标签: windows bash shell mingw32

在bash脚本中打开文件描述符并逐行读取文件时,脚本在处理70K行后终止并出现内存分配错误:

  

xmalloc:无法分配11541个字节(分配0个字节)

环境: MINGW32 Bash:3.1.20(4)-release(i686-pc-msys) 操作系统:Windows 7

输入文件的大小:每个1.2 GB

脚本如下:

#!/bin/bash
echo Left: $1
echo Right: $2
echo >"$1.diff"
echo >"$2.diff"
exec 4<"$1"
exec 5<"$2"
LINECOUNT=0
while [ $? == 0 ]
do
    exec 0<&4
    read LEFTLINE
    exec 0<&5
    read RIGHTLINE

    if [ $? != 0 ]
    then
        exit -1
    fi

    LINECOUNT=$(($LINECOUNT + 1))
    LINEMOD=$(($LINECOUNT % 1000))
    if [[ $LINEMOD == 0 ]]
    then
        echo Line: $LINECOUNT
    fi

    if [ $LEFTLINE != $RIGHTLINE ]
    then
        echo $LEFTLINE >> "$1.diff"
        echo $RIGHTLINE >> "$2.diff"
        echo Mismatch found
    fi
done

正如我上面所说,脚本工作了很长时间,处理大约70K行然后终止。我假设它终止,因为它耗尽了32位进程可以占用的所有内存。

脚本的目的是打开两个格式和长度相同的文件,并逐行比较。它会将两个输出文件创建到写出不匹配行的位置。我不得不编写脚本,因为我使用的所有比较工具都因“内存不足”错误而崩溃或被绞死。当我的剧本也崩溃时,我感到很惊讶。我不得不在C ++中重写它以使其工作。现在我试图理解为什么bash脚本失败了。从理论上讲,它不应该在内存中累积文件内容。相反,它应该一次读取一行并推进文件指针。我试图理解为什么它崩溃了。也许还有另一种解决我的问题的方法,你可以推荐我可以实现为bash脚本。

更新:测试了以下修改。它也崩溃了。

while IFS= read -u4 -r LEFTLINE && IFS= read -u5 -r RIGHTLINE
do
    LINECOUNT=$(($LINECOUNT + 1))
    LINEMOD=$(($LINECOUNT % 1000))

1 个答案:

答案 0 :(得分:0)

通过评论中的人们对问题的宝贵意见,找到了解决方案。 Petesh正确地评论说以前版本的bash中存在导致内存泄漏的错误(或许多错误)Here是Petesh提供的票证的链接。幸运的是,泄漏是在更新版本的bash中修复的。 所以解决方案是更新bash。我安装了cygwin和bash版本4.1.17(9)-release(i686-pc-cygwin)并且我的脚本成功完成,只消耗了1.5 Mb的内存记忆增加。 John Zwinch还测试了Bash 4.1.5,x86_64,并确认该版本中的错误也得到了修复。

在解决问题的同时,Mark Setchell和John Zwinck建议对脚本进行一些改进。这些修改并没有解决问题,但使用不同的文件格式使脚本更简单,更可靠。该脚本的最终版本如下:

#!/bin/bash
echo Left: $1
echo Right: $2
>"$1.diff"
>"$2.diff"
LINECOUNT=0
while IFS= read -u4 -r LEFTLINE && IFS= read -u5 -r RIGHTLINE
do
    LINECOUNT=$(($LINECOUNT + 1))
    LINEMOD=$(($LINECOUNT % 1000))
    if [[ $LINEMOD == 0 ]]
    then
        echo Line: $LINECOUNT
    fi

    if [ "$LEFTLINE" != "$RIGHTLINE" ]
    then
        echo $LEFTLINE >> "$1.diff"
        echo $RIGHTLINE >> "$2.diff"
        echo Mismatch found
    fi
done 4<"$1" 5<"$2"