#!/bin/bash
LIST=/errors_exception.txt
cd /test
for PATTERN in `cat $LIST`
do
for FILE in $(ls)
do
if zcat $FILE | grep -Fxq "$PATTERN"; then
echo "$PATTERN found pattern in $FILE" >> output
fi
done
done
我正在尝试扫描大量压缩日志文件(.gz)并检查我正在寻找的模式是否仍然存在于这些日志中。
例如,在我上面的代码中,可以说,errors_exception.txt
包含以下内容
one
one two three
four five
six
/test
- 目录包含日志文件
为什么当我运行脚本时,它没有将第二行“一二三”读取为一行?
当我运行 bash -x test.sh (脚本名称)时,它会读取第二行,就像文本文件中还有另外 3 行一样,它将“一二三”显示为一行。
答案1
list=/errors_exception.txt
cd /test
while IFS= read -r pattern ; do
for file in * ; do
if zcat < "$file" | grep -Fxq "$pattern"; then
echo "$pattern found pattern in $file"
fi
done
done <"$list" > output
笔记:
下面两行都不会达到您的预期:
for PATTERN in `cat $LIST` for FILE in $(ls)
在这两种情况下,shell 都会进行您不希望的分词。上面建议的代码避免了这种情况。
该文件
errors_exception.txt
真的在根目录中吗?我将变量转换为小写。这是用户创建变量的约定。此约定将防止您意外覆盖某些关键的 shell 参数。
有关分词的更多信息
当shell执行时:
for PATTERN in `cat $LIST`
它运行cat $LIST
。当它这样做时,空格、制表符和回车符都被视为同一件事:断字。因此,实际上,在分词之后,这一行变成:
for PATTERN in one one two three four five six
并且,当for
循环执行时,PATTERN
被依次分配为一、一、二、三、四、五和六。
您真正想要的是将每一行视为一行。这就是while read.... done<"$list"
使用该构造的原因:在每个循环上,它读取一整行。
如果任何文件名中含有空格,此行也会发生同样的问题:
for FILE in $(ls)
的结果ls
将被替换到行中,如果任何文件名中包含空格、制表符或回车符(所有这些都是合法字符),则名称将被拆分为多个部分。例如,在一个空目录中创建一个文件:
$ touch "a b c"
现在,运行一个for
循环:
$ for file in $(ls); do echo $file; done
a
b
c
for
即使只有一个文件,循环也会运行三次。这是因为文件名中有空格,并且在分词之后,for
循环获得三个参数:a、b 和 c。
这很容易避免。改用:
for file in *
shell 足够智能,可以保持每个文件名完好无损,无论其名称中包含哪些字符。
递归搜索
如果我们还想在子目录中搜索 gzip 压缩文件,那么我们可以使用 bash 的 globstar 功能,如下所示:
list=/errors_exception.txt
cd /test
shopt -s globstar
while IFS= read -r pattern ; do
for file in **/*.gz ; do
if zcat < "$file" | grep -Fxq "$pattern"; then
echo "$pattern found pattern in $file"
fi
done
done <"$list" > output
这需要bash
.