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

ループの1つの反復では、while値は$LINE同時に存在できません。Host そして denied。ファイルのデータを考慮すると、これは不可能です。だから、出力が得られないのです。

これら2つの単語に一致するファイルのすべての行を表示するにはHost または deniedgrep代わりに以下を使用してください。

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

ここで使用されているオプションは、正規表現の一致()ではなく文字列比較を実行し、部分文字列()の-F代わりに完全な単語と一致することを保証します。-w2 つのクエリ文字列が与えられたら、-e次を含むすべての行を取得します。どのこれら。

両方の単語がファイルに表示される場合にのみ、両方の単語を含む行のみを表示するより高度なクエリを実行するには、プログラムを使用して実行できますawk

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

ここで文字列と一致する行を見つけたら、Host文字列と同じように保存しますAccess denied。最後に、両方の文字列に何かが含まれている場合はそれを印刷します。

やや同等のシェルコードで:

#!/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"

より大きなファイルの場合は、より高速な(外部ファイルの場合)sedを使用できるとしますHostdenied

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

Hostこのソリューションは、で始まる最初の行を厳密に印刷することを理解してください。次のような(同じ行ではありません)最初単語を含む行ですdenied

抽出が必要な場合一部Host- の場合 a に変更され、deniedループが再開されます。qb

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

awkの同様の解決策は、Host1行前の最後の行をペア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

ロジックはシェルでも同じです(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通常の文字を1つだけ含むファイル全体を検索します。
  • - 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

関連情報