我有以下 bash 脚本:
#!/usr/bin/env bash
grep -e '^[a-zA-Z]\{4,8\}$' data/words3.txt | tr '[:upper:]' '[:lower:]' | sort -u > data/passphrase-words.txt
function wordfrequency() {
awk '{ for (i=1; i<=NF; i++) { word = tolower($i); words[word]++ } } END { for (w in words) printf("%3d %s\n", words[w], w) } ' | sort -rn
}
function getArticleText() {
awk '/<text xml:space="preserve">/,/<\/text>/' | sed 's/<.*>//'
}
function reduceWikiText() {
tr ' [:punct:]' '[\n*]' | sed '/^$/d' | tr '[:upper:]' '[:lower:]'
}
bzcat data/enwiki-20161020-pages-articles.xml.bz2 | getArticleText | reduceWikiText | grep -F -f data/passphrase-words.txt | wordfrequency > data/wordFreqs.txt
我确信可以通过多种方式简化它,但这就是我想出的。 data/passphrase-words 是大约 170k 个单词的列表,每行一个单词。data/enwiki-*
是 12GB 的压缩 XML(它是维基百科转储)。从那里, getArticleText 抓取每篇文章中的文本,reduceWikiText 将该文本“减少”为每行一个单词,并删除所有 xml 和标点符号,而词频则计算每个单词出现的频率。
如果我正确地阅读我的任务管理器,则 wordFrequency() 内的 gawk 正在使用大量内存; 695MB,如果我让它运行足够长的时间,超过 1GB RAM。
不属于任何函数的 grep 命令限制了 gawk 将看到的不同单词的数量,并且它占用恒定的 36 MB。我可以看到 gawk 需要 50MB 甚至 100MB,但超过 1GB?这似乎是错误的。增长率意味着它将无限增长。
我需要弄清楚为什么 gawk 使用这么多内存?由于 BZ2 文件的大小,我不能让 gawk 失控太多......
我不使用 sort | 的原因uniq-c| sort -nr 是因为我真的希望字数统计聚合发生在内存中;我知道它适合我正在处理的字数。更少的 HDD 使用 = 更快,对吗?
作为参考,适用于 Windows 的 Linux 子系统,以及:
$ gawk --version
GNU Awk 4.0.1
Copyright (C) 1989, 1991-2012 Free Software Foundation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
编辑:将我得到的内容(减去 12GB .xml.bz2 文件)发布在https://github.com/proegssilb/wikipedia-wordcount。根据评论中的建议,使用 mawk 似乎没有做任何事情,但我在 200MB RAM 时停止了该过程。让进程运行一整夜而不使用 awk,只是为了看看会发生什么。
编辑2:替换| sort | uniq -c
有问题的 awk 后,该过程在我外出的 6-7 小时内完成。我将做一些进一步的调整,尝试去掉文章中的 HTML 使用(摆脱造成如此多污染的“””),并再次计时,但至少现在它运行在“合理”的时间内。
答案1
所以,有一些事情有帮助,但让这个工作起作用的主要事情是使用sort | uniq -c
而不是 gawk,根据格雷戈里·尼斯贝特。
我也最终使用@dave_thompson_085 的评论关于tr -sc '[:alpha:]' '\n'
。该标志-s
结合了重复,这意味着我不必删除空行,并-c
反转要查找的字符集。的一个副作用-c
是你只能使用一个替换字符,而不是一组。也感谢戴夫关于 grep 和精确行匹配的问题( -x
)。如果我有投票赞成该评论的声誉,我会的。
最后,我不得不使用一些额外的代码来删除 XML 实体 ( "
) 并删除 html(多余的<ref />
)。在 中getArticleText
,新的 sed 命令是| sed -e 's/"/"/g' -e 's/</</g' -e 's/>/>/g' -e 's/&/&/g' -e 's/<.*>//g'
。每个表达式(-e
链接命令)处理不同的 HTML 实体。我尝试了一些更完整的选项(比如使用 perl堆栈溢出),但由于机器特定的问题,它们在我的情况下不起作用。最终脚本可以在我的字数库。
该脚本在我的机器上花了 3 小时 20 分钟完成,但它也是多年前的 6 核 AMD 硬盘。你的里程可能会有所不同,但这对我来说已经足够了。
我将避免接受这个答案,这样如果@Gregory Nisbet 或@dave_thompson_085 想发布他们自己的答案,他们就可以。