查找多个文件中字符串的最后一次出现


9

我需要搜索多个日志文件(过去24小时内生成的所有文件,都保存在同一目录中)以查找字符串的最后一次出现。这是我写的命令:

find . -mtime 1 | grep fileprefix | xargs grep 'search string' | tail -1

但这仅返回一个文件的最后一行。关于如何调整所有线条的任何建议?


您是否尝试过转换尾巴和最后一个grep?找 。-mtime 1 | grep fileprefix | xargs tail -1 | grep“搜索字符串”
Mathieu 2015年

1
另请参阅:在所有子目录中
找到

Answers:


4

假设GNU设施:

find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +

您能否详细说明“ bash -c \”的用途,因为我已经在使用bash shell了。最后还要使用“ _ {} +”。
罗克什

@Lokesh,您可以使用find来对文件执行命令-exec。使用bash -c,我们产生了一个bashshell,该shell循环遍历由它们找到findtac .. | grep -m1 fileprefix在每个文件上执行的文件
iruvar 2015年

我试图通过包括cut命令(例如f)来扩展for循环中的字符串过滤。做TAC“ $ F” | grep -m1文件前缀| cut -d''-f4,7-8,但是当我输入cut命令时,它给我错误的文件意外结尾。你能建议我做错了吗?
罗克什

@lokesh,-d" "与cut 一起使用。双引号代替单引号
iruvar

1
find命令可以过滤文件前缀。该grep不需要的。同样令人惊讶的是,搜索字符串未包含在此答案中。
乔纳森·莱夫勒

8

如果所有内容都在一个目录中,则可以执行以下操作:

for file in *fileprefix*; do
    grep 'search string' "$file" | tail -1
done

如果这些文件很大,则可能有必要通过tac以相反的顺序打印文件(最后一行在第一行)然后grep -m1匹配第一个出现的文件来加快处理速度。这样,您就不必读取整个文件:

for file in *fileprefix*; do
    tac file | grep -m1 'search string'
done

两者都假定没有匹配的目录fileprefix。如果有,您将收到一个错误,可以忽略。如果这是一个问题,请仅检查文件:

 for file in *fileprefix*; do
    [ -f "$file" ] && tac file | grep -m1 'search string'
 done

如果还需要打印文件名,请添加-H到每个grep调用中。或者,如果您grep不支持它,请告诉它也搜索/dev/null。这不会改变输出,但是由于grep提供了多个文件,因此它将始终为每次匹配显示文件名:

for file in *fileprefix*; do
    grep 'search string' "$file" /dev/null | tail -1
done

“那样,您就不必读取整个文件” –嗯?不,您可以避免在grep中读取整个文件,而是将整个文件通过tac放入。对我来说不清楚这会更快,尽管这取决于匹配是在文件的开头还是结尾附近。
吉尔(Gilles)'所以

@吉尔斯,不,您不会将整个文件都放入tac其中。一旦找到第一个比赛,它将退出。我刚刚测试了832M文本文件和在最后一行找到的模式。grep -m 1 pattern file工具约7秒钟,tac file | grep -m1 pattern耗时0.009
terdon

4
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
     -exec sed -se'/searchstring/h;$!d;x' {} +

如果您拥有sed支持-separate files选项和POSIX的GNU ,它将起作用find

不过,您可能应该添加! -type d-type f限定符,因为尝试读取目录不会很有用,并且进一步将范围缩小到常规文件可以避免读取挂在管道或串行设备文件上。

逻辑非常简单- sedh匹配的任何输入行的副本覆盖旧空间searchstring,然后d从输出中删除所有输入行,但删除每个输入文件的最后一行。当到达最后一行时,它将x更改其保留空间和模式空间,因此,如果searchstring在读取文件时完全找到它,则最后一次出现的情况将自动打印输出,否则将写入空白行。(如果不希望的话,请添加/./!dsed脚本的末尾)

这将对sed大约65k个输入文件执行一次调用-或您的ARG_MAX限制是多少。这应该是一个非常高效的解决方案,并且非常容易实现。

如果您还想要文件名,则在给定最新GNU的情况下,sed您可以使用F命令将它们写到单独的行中,否则,可以通过find在后面附加-print主文件来在每批的单独列表中打印它们+


1

怎么样:

find . -mtime -1 -name "fileprefix*" -exec sh -c \
'echo "$(grep 'search string' $1 | tail -n 1),$1"' _ {} \;

上面的代码为您提供了一个不错的输出,每个文件中最后出现一个搜索字符串,后跟逗号后的相应文件名(修改echo之下的“,$ 1”部分以更改格式,或者在不必要时将其删除)。在带有“文件”名称前缀的文件中搜索“ 10”搜索字符串的示例输出如下:

[dmitry@localhost sourceDir]$ find . -mtime -1 -name "file*" -exec  sh -c 'echo "$(grep '10' $1 | tail -n 1),$1"' _ {} \;
Another data 02 10,./file02.log
Some data 01 10,./file01.log
Yet another data 03 10,./file03.log 

1
find . -mtime 1 -name 'fileprefix*' -exec grep -Hn 'search string' {} + |
    sort -t: -k1,2 -n | 
    awk -F: '{key=$1 ; $1="" ; $2="" ; gsub(/^  /,"",$0); a[key]=$0} 
             END {for (key in a) { print key ":" a[key] }}'

它使用GNU grep-H-n选择总是同时打印文件名和所有匹配的行号,然后它按文件名和行号,和管道入AWK,这对于数组中的每个文件名的最后一场比赛专卖店,并最终打印它。

一种蛮力的方法,但是可以用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.