Bash脚本:使用不同行的“AND”条件读取文本文件

Bash脚本:使用不同行的“AND”条件读取文本文件

我创建了带有“AND”条件的简单 bash 脚本,但不起作用:

#!/bin/bash

cat log3.txt | \
while read -r LINE
do
  if [[ $LINE =~ Host ]] && [[ $LINE =~ denied ]]  ; then echo $LINE;
fi
done

这是 log3.txt 的内容

Host: abcd.com
Access denied

如果使用 OR 条件,它运行良好,但我想使用 AND 条件,因此如果日志同时包含字符串“Host”和“Access Denied”,我将得到输出。

答案1

在循环的任何一次迭代中while,该值$LINE不能同时存在Host denied。考虑到文件中的数据,这是不可能的。这就是为什么你没有得到输出。

如果您想查看文件中与这两个单词匹配的所有行Host 或者 deniedgrep改为使用:

grep -wF -e 'Host' -e 'Access denied' <log3.txt

这里使用的选项将确保我们进行字符串比较而不是正则表达式匹配 ( -F),并且我们匹配完整的单词而不是子字符串 ( -w)。给出两个查询字符串-e,我们将得到包含以下内容的任何行任何这些。

如果您想进行稍微更高级的查询,仅显示包含两个单词(如果它们都出现在文件中)的行,那么您可以使用程序来完成awk

awk '/Host/ { hostline=$0 } /Access denied/ { deniedline=$0 }
     END { if ((hostline != "") && (deniedline != ""))
               print hostline; print deniedline; }' <log3.txt

在这里,如果我们找到与 string 匹配的行Host,我们将保存它,对于 string 也是如此Access denied。最后,如果两个字符串都包含任何内容,我们将打印它们。

在或多或少等效的 shell 代码中:

#!/bin/sh

while IFS= read -r line; do
    case $line in
        *Host*)
           hostline=$line   ;;
        *"Access denied"*)
           deniedline=$line ;;
    esac
done <log3.txt

if [ -n "$hostline" ] && [ -n "$deniedline" ]; then
    printf '%s\n%s\n' "$hostline" "$deniedline"
fi

这里我用一条case ... esac语句对读取到的数据进行匹配。使用的模式是文件名通配模式,而不是正则表达式。

有关的:

答案2

如果您确定要处理的文件很小(如您的示例),您可以一次性读取整个文件并测试:

 file=$(<log3.txt)
 [[ $file =~ Host ]] && [[ $file =~ denied ]] && echo "$file"

对于较大的文件,并假设Hostdenied可以使用更快的(对于外部文件)sed:

 <log3.txt sed -n '/^Host/!d;p;:1;n;/\<denied\>/{p;q};b1'

了解此解决方案将严格打印以 和 开头的第一Host下列的(不在同一行)第一的包含denied单词的行。

如果您需要提取一些Host- denied,将 更改q为 a b,这将重新开始循环:

 <log3.txt sed -n '/^Host/!d;p;:1;n;/\<denied\>/{p;b};b1'

awk 的类似解决方案将打印Host一行之前的最后一行denied(成对):

 awk  '  p==1 && /\<denied\>/     {d=$0;p=0}
                /^Host*/         {h=$0;p=1}
         { if(p==0&&h!=""&&d!="") {print h,RS,d;p=2} }
      '  <log3.txt

shell 中的逻辑相同(除了它会匹配denied行上的任何位置(而不是单词)):

 #!/bin/sh
 p=0
 while IFS= read -r line; do
    if [ "$p" = 1 ]; then
        case $line in
           *denied*)        deniedline=$line; p=0   ;;
        esac
    fi

 case $line in
    Host*)               hostline=$line; p=1   ;;
 esac

 if [ "$p" = 0 ] && [ "$hostline" ] && [ "$deniedline" ]; then
    printf '%s\n%s\n' "$hostline" "$deniedline"
    p=2
 fi

 done <log3.txt

答案3

请参阅@Kusalananda 答案,了解为什么您的解决方案不起作用。

我的解决方案使用grep -z

grep -zEo -e 'Host: (\w|\.)+\s+Access denied\s' log.txt

慢动作:

  • -E:使用扩展正则表达式
  • -o:仅打印匹配项
  • -z: 用作\0行分隔符。由于没有,搜索是在整个文件上完成的,其中\n只是一个普通字符。
  • - e'Host: (\w|\.)+\s+Access denied\s': 寻找:
    • Host:
    • 字母、数字或点的序列
    • 太空级角色(将是\n
    • Access denied
    • 一个太空级角色(将是\n)。这个需要在输出上换行

运行于:

Host: denied1.com
Access denied
Host: ok.com
Access OK
Host: random.com

Host: denied2.com
Access denied

More stuff

产量:

Host: denied1.com
Access denied
Host: denied2.com
Access denied

相关内容