如何从文件模式开始grep所有行

时间:2018-10-22 11:29:03

标签: shell unix grep

我有一个Nginx日志文件,我想根据Ips分成多个文件。例如,我有ips1.txtips2.txt。每个文件具有日志文件唯一ip数量的一半。 Nginx日志文件具有以下格式:

172.0.0.10 - [24/Jun/2018:11:00:00 +0000] url1 GET url2 HTTP/1.1 (200) 0.000 s 2356204 b url3 - - [HIT] - s - Mozilla/5.0 (X11; CrOS x86_64 10452.99.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.203 Safari/537.36 

172.0.0.11 - [24/Jun/2018:11:00:00 +0000] url1 GET url2 HTTP/1.1 (200) 0.000 s 307 b url3 - - [HIT] - s - Mozilla/5.0 (X11; CrOS x86_64 10452.99.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.203 Safari/537.36

因此,我要获取所有以模式文件中的IP开头的行的操作是

cat log.txt | grep -f ips1.txt > part1.txt
cat log.txt | grep -f ips2.txt > part2.txt

我知道我正在做的grep在整个行中搜索,而不仅仅是在开始时。这使搜索速度变慢,浪费了更多的内存。我知道是否只有一种模式可以使用awk(例如awk '{if($1 == "172.0.0.10")print;}' log.txt),但是我不知道如何使用grep使用模式文件来实现。

因此,我想要的是减少浪费的存储空间,并通过仅在行的开头进行查找来加快搜索速度。我的日志文件有很多GB,如果可以的话,我会节省很多时间。

编辑:

我的ips * .txt文件是根据我拥有的线程数生成的。您可以在下面看到我的代码如何:

NUM_THREADS=8
export LC_ALL=C

unpigz -c log.gz | awk '{print $1;}' | LC_ALL=C sort -S 20% -u > all_ips.txt

lines_arq=$(wc -l all_ips.txt | cut -d' ' -f1)
lines_each_file=$(($lines_arq / $NUM_THREADS + 50))
split --lines=$lines_each_file all_ips.txt 2018/prefixo.

zgrep log.gz -Fwf 2018/prefixo.aa | pigz > file1.gz &
zgrep log.gz -Fwf 2018/prefixo.ab | pigz > file2.gz &
...
zgrep log.gz -Fwf 2018/prefixo.ah | pigz > file8.gz &

wait

unpigz -c file1.gz | pypy script.py -i - -o onOff -s 11:00:00 -m testing -y 2018 | pigz > onOff-file1.gz &
...
unpigz -c file8.gz | pypy script.py -i - -o onOff -s 11:00:00 -m testing -y 2018 | pigz > onOff-file8.gz &

2 个答案:

答案 0 :(得分:2)

整个过程都使用awk。首先阅读固定的字符串,然后拆分日志。例如:

awk '{out[$1] = FILENAME ".out"} 
     END {while (getline < input) { print > out[$1] }}
' input=log.txt ips[12].txt

多次读取输入文件将大大损害您的性能,而不是不必要地awk拆分行的开销。

下面是代码的简要说明。第一个(也是唯一的)命令是读取输入并构建文件名数组。列出所有ips * .txt作为输入,因此将这些行读入数组。理想情况下,这些文件相对较小,因此构建此阵列不会花费很多精力。构建阵列后,您输入END子句,在其中读取日志文件(仅一次!),并将每一行写入相应的文件。

似乎您想动态生成ips * .txt,并且只想分发日志。在这种情况下,请尝试以下操作:

awk '! ($1 in out) {out[$1] = (idx++ %10) } 
    { outfile= "output." out[$1] ".txt"; print > outfile ; next} ' log.txt

这只是检查您是否已经看过ip:如果已经看过ip,然后将其写到与上一个日志相同的文件中。如果没有,增加一个计数器(模数10 ...根据所需的文件数量选择模数)并写入该文件,记录您要在哪行写入行。对日志中的每一行重复。

这里的关键是最大程度地减少您阅读日志的次数。

答案 1 :(得分:2)

这里有一些加快指令速度的想法。确保对它们进行基准测试。我丢失了数据以自己对它们进行基准测试。

  • zgrep file上使用unpigz -c file | grep
  • 使用快速语言环境:LC_ALL=C zgrep ...
  • 使用固定字符串搜索-F和单词正则表达式-w。固定字符串搜索应该比默认的基本正则表达式搜索快一点。对于固定的字符串大小写,单词正则表达式是最接近您»搜索的内容,仅在行的开头
    grep -Fwf ip...

  • 将ip文件编译为正则表达式,并将^添加到开头以仅在行的开头进行搜索。然后使用grep -Egrep -P "$regex" / pcregrep "$regex"-E-P的速度可以相差很多。选中两者,看看哪一个更快。
regex="$(tr \\n \| < ips1.txt | sed 's/^/^(/;s/\./\\./g;s/$/)/')"
zgrep -E "$regex" yourfile > part1.txt
zgrep -Ev "$regex" yourfile > part2.txt