我有几十个文件夹,其中包含纯文本日志文件和经过 gzip 压缩的旧日志。我的目标是只运行一行代码(一次一个文件夹),根据时间戳顺序提取所有 grep 结果,无论包含匹配的日志文件是 txt 还是 gz,并尽可能优化性能。
这对于纯文本文件来说很有效:
ls -rt log.*.txt | xargs grep <treasure> -
我使用这个而不是 grep,因此结果按文件创建的时间顺序排序,可能跨越多天,而不是根据文件名排序。文件名 (log.#.txt) 增长到某个整数限制,然后换行到 log.0.txt,但这可以跨越 24 小时标记,也可以不跨越。
一旦 txt 文件打包,旧文件将被 gz 压缩:log.#.archive.gz。仅保留整数限制的 gz 文件。
我考虑使用 if/fi 语句根据当前文件的扩展名来 grep 或 zgrep。但是,我尝试在 gz 文件上执行此操作的第一步没有成功:
ls -rt log.*.gz | xargs zgrep <treasure> -
我收到一堆错误“未找到文件‘treasure’”(每个.gz 文件一个)
我也尝试过
ls -rt "log.*.gz" | xargs -0 zgrep <treasure> -
得到相同的结果。我知道这一定是因为我对 xargs 命令的了解不够深入。也许我甚至可以通过适当的 grep/zgrep 选项、find 或其他完全不同的方法来实现这一点。
答案1
以下列出一些错误之处:
尽量不要迭代或将 产生的输出管道传输
ls
到另一个工具中。如果文件包含空格或换行符,它将中断,具体取决于命令的构造方式。但是,就你的情况而言,除了使用 之外,没有其他简单的方法可以实现你想要的效果ls
。因此,如果你知道你的文件名不包含换行符,那么就没问题了。<treasure> -
将被 shell 解释为重定向。第一个括号<
将被 shell 读取,意思是“从名为 的文件读取 STDINtreasure
”。第二个括号将被读取为> -
,即“将 STDOUT 写入名为 的文件-
”。因此,您应该正确引用您尝试读取的模式:grep "<treasure>"
。选项
-0
将xargs
输入读取为 ASCII NUL 分隔行,ls
不会产生任何结果。它仅与可以创建 NUL 分隔输出的工具结合使用才有用,例如find
与-print0
选项结合使用。-
我不明白你的命令的目的。
因此,尝试这样的操作:
shopt -s extglob
ls -rt1 +(log.*.txt|log.*.gz) | xargs zgrep "<treasure>"
或者:
ls -rt1 +(log.*.txt|log.*.gz) | xargs -L1 zgrep "<treasure>"
解释:
extglob
允许两个文件扩展名匹配-1
使ls
输出每行一个文件+(…|…)
表示“一个或多个”模式- 如果您使用
-L1
,则每次xargs
只会传递一个文件zgrep
。但这可能不是您想要的。
答案2
关于什么:
for each in `ls -rt log.*.gz`; do zgrep "<TREASURE>" $each; done