「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
または denied
、grep
代わりに以下を使用してください。
grep -wF -e 'Host' -e 'Access denied' <log3.txt
ここで使用されているオプションは、正規表現の一致()ではなく文字列比較を実行し、部分文字列()の-F
代わりに完全な単語と一致することを保証します。-w
2 つのクエリ文字列が与えられたら、-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を使用できるとしますHost
。denied
<log3.txt sed -n '/^Host/!d;p;:1;n;/\<denied\>/{p;q};b1'
Host
このソリューションは、で始まる最初の行を厳密に印刷することを理解してください。次のような(同じ行ではありません)最初単語を含む行ですdenied
。
抽出が必要な場合一部Host
- の場合 a に変更され、denied
ループが再開されます。q
b
<log3.txt sed -n '/^Host/!d;p;:1;n;/\<denied\>/{p;b};b1'
awkの同様の解決策は、Host
1行前の最後の行をペア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