我有一个系统生成非常大的文本日志(每个超过1GB)。我正在为它们提供的实用程序要求每个文件小于500MB。我不能简单地使用split命令,因为这会冒一个将日志条目分成两半的风险,这会导致它们被输入的实用程序出错。
我对split,csplit和awk做了一些研究。到目前为止,我运气最好的是:
awk '/REG_EX/{if(NR%X >= (X-Y) || NR%2000 <= Y)x="split"++i;}{print > x;}' logFile.txt
在上面的示例中,X表示我希望每个拆分文件包含的行数。在实践中,这最终约为1000万。 Y代表“加或减”。因此,如果我想要“1000万加或减50”,Y允许这样做。
我使用的实际正则表达式并不重要,因为该部分有效。目标是每隔X行拆分文件,但前提是它只是REG_EX的出现。这是if()子句的用武之地。我试图有一些正或负Y线的“摆动空间”,因为无法保证REG_EX将完全存在于NR%X处。我的问题是,如果我将Y设置得太小,那么我最终得到的文件数量是我瞄准的行数的两到三倍。如果我将Y设置得太大,那么我最终会得到一些包含1到X行之间的文件(REG_EX可能会连续发生几次)。
如果没有编写我自己的程序来逐行遍历文件计数器,我怎样才能优雅地解决这个问题呢?我有一个同事创建的脚本,但需要一个多小时才能完成。我的awk命令在1.5GB文件上以不到60秒的时间完成,X值为1000万,但不是100%的解决方案。
==编辑==
找到解决方案。感谢所有花时间阅读我的问题,理解并提供建议解决方案的人。他们中的大多数都非常有帮助,但我标记为解决方案的人提供了最大的帮助。我的问题是我的模块化数学是截止点。我需要一种方法来跟踪行并在每次分割文件时重置计数器。作为awk的新手,我不确定如何使用BEGIN{ ... }
功能。让我总结一下问题集,然后列出解决问题的命令。
问题:
- 系统生成文本日志&gt; 1.5GB
- 输入日志的系统需要日志<= 500MB
- 每个日志条目都以标准化行开头
- 使用split命令会冒一个新文件的开头没有标准行
要求:
- 在第X行分割文件,但是
- IFF Xth行采用标准日志条目格式
注意:
- 日志条目的长度各不相同,有些是完全空的
解决方案:
awk 'BEGIN {min_line=10000000; curr_line=1; new_file="split1"; suff=1;} \
/REG_EX/ \
{if(curr_line >= min_line){new_file="split"++suff; curr_line=1;}} \
{++curr_line; print > new_file;}' logFile.txt
该命令可以在一行输入;为了便于阅读,我把它分解了。一千万行可以达到450MB到500MB之间。我意识到,考虑到标准日志输入行的出现频率,我不需要设置上限限制,只要我选择了一个有限空间的下限。每次REG_EX匹配时,它会检查当前行数是否大于我的限制,如果是,则启动一个新文件并重置我的计数器。
再次感谢大家。我希望碰到这个或类似问题的其他任何人都觉得这很有用。
答案 0 :(得分:1)
如果要根据模式出现的确切n次计数创建拆分文件,可以这样做:
awk '/^MYREGEX/ {++i; if(i%3==1){++j}} {print > "splitfilename"j}' logfile.log
其中:
^MYREGEX
是您想要的模式。3
是模式的计数
你想要在每个文件中出现。splitfilename
是前缀
要创建的文件名。logfile.log
是您的输入日志文件。i
是一个计数器,每次出现模式时都会递增。j
是一个计数器,对于每个第n次出现的模式递增。示例:
$ cat test.log
MY
123
ksdjfkdjk
MY
234
23
MY
345
MY
MY
456
MY
MY
xyz
xyz
MY
something
$ awk '/^MY/ {++i; if(i%3==1){++j}} {print > "file"j}' test.log
$ ls
file1 file2 file3 test.log
$ head file*
==> file1 <==
MY
123
ksdjfkdjk
MY
234
23
MY
345
==> file2 <==
MY
MY
456
MY
==> file3 <==
MY
xyz
xyz
MY
something
答案 1 :(得分:0)
您可能会将日志文件拆分1000万行。 然后,如果第二个分割文件没有以所需的行开始,则在第一个分割文件中找到最后一个所需的行,从那里删除该行和后续行,然后将这些行添加到第二个文件。 对每个后续拆分文件重复上述操作。
这将生成与正则表达式匹配计数非常相似的文件。
为了提高性能而不必实际编写中间拆分文件并对其进行编辑,您可以使用pt-fifo-split之类的工具来实现&#34;虚拟&#34;拆分原始日志文件。
答案 2 :(得分:0)
如果基于正则表达式的拆分不重要,一个选项是逐行创建新文件,跟踪要添加到输出文件的字符数。如果字符数大于某个阈值,则可以开始输出到下一个文件。示例命令行脚本是:
cat logfile.txt | awk 'BEGIN{sum=0; suff=1; new_file="tmp1"} {len=length($0); if ((sum + len) > 500000000) { ++suff; new_file = "tmp"suff; sum = 0} sum += len; print $0 > new_file}'
在此脚本中,sum
会跟踪我们从给定日志文件中解析的字符数。如果sum
在500 MB范围内,则会一直输出到tmp1
。一旦sum
即将超过该限制,它将开始输出到tmp2
,依此类推。
此脚本不会创建大于限制大小的文件。它也不会破坏日志条目。
请注意,此脚本不会使用您在脚本中使用的任何模式匹配。
答案 3 :(得分:0)
将fout
和slimit
值替换为您的需求
#!/bin/bash
# big log filename
f="test.txt"
fout="$(mktemp -p . f_XXXXX)"
fsize=0
slimit=2500
while read line; do
if [ "$fsize" -le "$slimit" ]; then
# append to log file and get line size at the same time ;-)
lsize=$(echo "$line" | tee -a $fout | wc -c)
# add to file size
fsize=$(( $fsize + $lsize ))
else
echo "size of last file $fout: $fsize"
# create a new log file
fout="$(mktemp -p . f_XXXXX)"
# reset size counter
fsize=0
fi
done < <(grep 'YOUR_REGEXP' "$f")
size of last file ./f_GrWgD: 2537
size of last file ./f_E0n7E: 2547
size of last file ./f_do2AM: 2586
size of last file ./f_lwwhI: 2548
size of last file ./f_4D09V: 2575
size of last file ./f_ZuNBE: 2546