从 Bash 脚本逐行读取 2gb 文件导致 "xmalloc:cannot allocate..." 错误
Reading 2gb files line by line from Bash script results in "xmalloc:cannot allocate..." error
在 bash 脚本中打开文件描述符并逐行读取文件时,脚本在处理 70K 行后因内存分配错误而终止:
xmalloc: cannot allocate 11541 bytes (0 bytes allocated)
环境:
MINGW32
Bash: 3.1.20(4)-release (i686-pc-msys)
OS: Windows 7
输入文件的大小:每个 1.2 GB
脚本如下:
#!/bin/bash
echo Left:
echo Right:
echo >".diff"
echo >".diff"
exec 4<""
exec 5<""
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 >> ".diff"
echo $RIGHTLINE >> ".diff"
echo Mismatch found
fi
done
正如我在上面所说的那样,该脚本可以运行很长时间,处理大约 70K 行然后终止。我认为它终止是因为它用完了 32 位进程可以占用的所有内存。
脚本的目的是打开两个相同格式和长度的文件,逐行比较。它在其中写出不匹配的行的地方创建了两个输出文件。我不得不编写脚本,因为我可以使用的所有比较工具都因 "out of memory" 错误而崩溃或挂起。当我的脚本也崩溃时,我感到很惊讶。我不得不用 C++ 重写相同的内容才能使其工作。现在我试图理解 bash 脚本失败的原因。理论上它不应该在内存中累积文件内容。相反,它应该一次只读取一行并推进文件指针。我试图理解它崩溃的原因。也许还有另一种方法可以解决我的问题,您可以推荐我可以将其实现为 bash 脚本。
更新: 测试了以下修改。也崩溃了。
while IFS= read -u4 -r LEFTLINE && IFS= read -u5 -r RIGHTLINE
do
LINECOUNT=$(($LINECOUNT + 1))
LINEMOD=$(($LINECOUNT % 1000))
根据人们对问题的评论提供的宝贵意见,我们找到了解决方案。 Petesh 正确评论 bash 的先前版本中存在一个(或多个)错误导致内存泄漏。 Here 是 Petesh 提供的票证的 link。幸运的是,漏洞已在 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:
echo Right:
>".diff"
>".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 >> ".diff"
echo $RIGHTLINE >> ".diff"
echo Mismatch found
fi
done 4<"" 5<""
在 bash 脚本中打开文件描述符并逐行读取文件时,脚本在处理 70K 行后因内存分配错误而终止:
xmalloc: cannot allocate 11541 bytes (0 bytes allocated)
环境: MINGW32 Bash: 3.1.20(4)-release (i686-pc-msys) OS: Windows 7
输入文件的大小:每个 1.2 GB
脚本如下:
#!/bin/bash
echo Left:
echo Right:
echo >".diff"
echo >".diff"
exec 4<""
exec 5<""
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 >> ".diff"
echo $RIGHTLINE >> ".diff"
echo Mismatch found
fi
done
正如我在上面所说的那样,该脚本可以运行很长时间,处理大约 70K 行然后终止。我认为它终止是因为它用完了 32 位进程可以占用的所有内存。
脚本的目的是打开两个相同格式和长度的文件,逐行比较。它在其中写出不匹配的行的地方创建了两个输出文件。我不得不编写脚本,因为我可以使用的所有比较工具都因 "out of memory" 错误而崩溃或挂起。当我的脚本也崩溃时,我感到很惊讶。我不得不用 C++ 重写相同的内容才能使其工作。现在我试图理解 bash 脚本失败的原因。理论上它不应该在内存中累积文件内容。相反,它应该一次只读取一行并推进文件指针。我试图理解它崩溃的原因。也许还有另一种方法可以解决我的问题,您可以推荐我可以将其实现为 bash 脚本。
更新: 测试了以下修改。也崩溃了。
while IFS= read -u4 -r LEFTLINE && IFS= read -u5 -r RIGHTLINE
do
LINECOUNT=$(($LINECOUNT + 1))
LINEMOD=$(($LINECOUNT % 1000))
根据人们对问题的评论提供的宝贵意见,我们找到了解决方案。 Petesh 正确评论 bash 的先前版本中存在一个(或多个)错误导致内存泄漏。 Here 是 Petesh 提供的票证的 link。幸运的是,漏洞已在 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:
echo Right:
>".diff"
>".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 >> ".diff"
echo $RIGHTLINE >> ".diff"
echo Mismatch found
fi
done 4<"" 5<""