从文件中 Grep 包含空格的字符串

从文件中 Grep 包含空格的字符串
#!/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.

相关内容