回答:
この操作にはGrepでは不十分です。
最近のほとんどのLinuxシステムにあるpcregrepは、次のように使用できます。
pcregrep -M 'abc.*(\n|.)*efg' test.txt
ここ-M
で--multiline
、パターンが複数の行に一致することを許可します
新しいpcre2grepもあります。どちらもPCREプロジェクトによって提供されます。
pcre2grepは、Mac OS X ではポートの一部としてMacポート経由で利用できますpcre2
。
% sudo port install pcre2
そしてHomebrew経由で:
% brew install pcre
またはpcre2の場合
% brew install pcre2
pcre2grepはLinux でも利用できます(Ubuntu 18.04+)
$ sudo apt install pcre2-utils # PCRE2
$ sudo apt install pcregrep # Older PCRE
-M, --multiline
パターンが複数の行に一致することを許可します。
'abc.*(\n|.)*?efg'
.*
-> 'abc(\n|.)*?efg'
正規表現を短くするために(そして知識を増やすために)
grepでそれが可能かどうかはわかりませんが、sedを使用すると非常に簡単になります。
sed -e '/abc/,/efg/!d' [file-with-content]
sed
、そのような表現を見たことがない場合は、
これはこの答えに触発された解決策です:
「abc」と「efg」が同じ行にある場合:
grep -zl 'abc.*efg' <your list of files>
「abc」と「efg」が異なる行にある必要がある場合:
grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
パラメータ:
-z
入力を一連の行として扱い、各行は改行ではなくゼロバイトで終了します。つまり、grepは入力を1つの大きな行として扱います。
-l
通常は出力が出力される各入力ファイルの名前を出力します。
(?s)
PCRE_DOTALLをアクティブにします。つまり、「。」任意の文字または改行を検索します。
l
。私の知る限り、番号の-1
オプションはありません。
-z
オプションが改行を扱うようにgrepを指定する場合、zero byte characters
なぜ(?s)
正規表現でが必要なのですか?すでに非改行文字である.
場合、直接一致させることはできませんか?
私はpcregrepに大きく依存していましたが、新しいgrepでは多くの機能のためにpcregrepをインストールする必要はありません。だけを使用してくださいgrep -P
。
OPの質問の例では、次のオプションがうまく機能すると思います。2番目の選択肢は、質問を理解する方法と一致しています。
grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*
テキストを/ tmp / test1としてコピーし、 'g'を削除して/ tmp / test2として保存しました。以下は、最初の文字列が一致した文字列を示し、2番目の文字列がファイル名のみを示していることを示す出力です(通常-oは一致を表示し、通常の-lはファイル名のみを表示します)。「z」は複数行に必要であり、「(。| \ n)」は「改行以外のもの」または「改行」のいずれかに一致することを意味します。
user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1
バージョンが十分に新しいかどうかを確認するには、実行してman grep
、これと同様のものが上部に表示されるかどうかを確認します。
-P, --perl-regexp
Interpret PATTERN as a Perl regular expression (PCRE, see
below). This is highly experimental and grep -P may warn of
unimplemented features.
それはGNU grep 2.10からです。
これは、最初にを使用tr
して改行を他の文字に置き換えることで簡単に実行できます。
tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'
ここで\a
は、改行の代わりにアラーム文字(ASCII 7)を使用しています。これはテキストではほとんど見られず、とgrep
一致させることも.
、特にと一致させることもできます\a
。
\0
していたので、必要に応じgrep -a
てマッチングを行い\x00
ました。echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
isecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
ます。
awkワンライナー:
awk '/abc/,/efg/' [file-with-content]
abc
、終了パターンがファイルに存在しない場合、または最後の終了パターンがない場合に、ファイルの最後から最後まで問題なく印刷されます。これは修正できますが、スクリプトがかなり複雑になります。
/efg/
出力から除外する方法は?
Perlを使用できる場合は、非常に簡単に行うことができます。
perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt
単一の正規表現でもこれを行うことができますが、これにはファイルの内容全体を単一の文字列に含める必要があるため、大きなファイルではメモリを消費しすぎる可能性があります。完全を期すために、そのメソッドは次のとおりです。
perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt
.*?
。最小一致を取得するには、貪欲でない一致()を使用する必要がありました。
grepでそれを行う方法はわかりませんが、awkで次のようにします。
awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo
ただし、これを行う方法には注意が必要です。正規表現を部分文字列または単語全体に一致させますか?必要に応じて\ wタグを追加します。また、これは例の記述方法に厳密に準拠していますが、efgの後にabcが2回目に表示された場合はまったく機能しません。これを処理したい場合は、/ abc /ケースなどに必要に応じてifを追加します。
数日前に、複数行マッチングまたは条件を使用してこれを直接サポートするgrepの代替案をリリースしました。この例のコマンドは次のようになります。
複数行:
sift -lm 'abc.*efg' testfile
条件:
sift -l 'abc' testfile --followed-by 'efg'
また、特定の行数内で「efg」が「abc」に続く必要があることを指定することもできます。
sift -l 'abc' testfile --followed-within 5:'efg'
sift-tool.orgで詳細を確認できます。
sift -lm 'abc.*efg' testfile
。なぜなら、マッチは貪欲efg
で、ファイルの最後まですべての行をゴブリンと飲み込むからです。
#!/bin/bash
shopt -s nullglob
for file in *
do
r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
if [ "$r" -eq 1 ];then
echo "Found pattern in $file"
else
echo "not found"
fi
done
パターンのシーケンスに詳しくない場合は、grepを使用できます。
grep -l "pattern1" filepattern*.* | xargs grep "pattern2"
例
grep -l "vector" *.cpp | xargs grep "map"
grep -l
最初のパターンに一致するすべてのファイルを検索し、xargsは2番目のパターンに対してgrepを実行します。お役に立てれば。
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
一致しません
Balu Mohanの回答の代わりにgrep
、head
とのみを使用してパターンの順序を強制することができtail
ます。
for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done
しかし、これはあまりきれいではありません。より読みやすくフォーマット:
for f in FILEGLOB; do
tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \
| grep -q "pattern2" \
&& echo $f
done
これは、の"pattern2"
後にある"pattern1"
、または両方が同じ行にあるすべてのファイルの名前を出力します。
$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt
tail -n +i
i
-th 以降のすべての行を出力しますgrep -n
-一致する行の先頭に行番号を追加しますhead -n1
-最初の行のみを印刷しますcut -d : -f 1
- :
区切り文字として使用して最初の切り取り列を印刷する2>/dev/null
- 式が空を返したtail
場合に発生する無音エラー出力$()
grep -q
- grep
終了コードにのみ関心があるため、一致が見つかった場合は黙ってすぐに戻る&>
か?私も使用していますが、どこにも文書化されているのを見たことがありません。ところで、なぜ実際にgrepを黙らせる必要があるのですか?grep -q
トリックもしませんか?
&>
標準出力と標準エラーの両方をリダイレクトするようにbashに指示します。bashマニュアルのREDIRECTIONを参照してください。良いキャッチのgrep -q ...
代わりに私たちも同じようにできるという点であなたはとても正しいですgrep ... &>/dev/null
!
filepattern *.sh
は、ディレクトリが検査されないようにするために重要です。もちろん、いくつかのテストはそれも防ぐことができます。
for f in *.sh
do
a=$( grep -n -m1 abc $f )
test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue
(( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done
の
grep -n -m1 abc $f
最大1つの一致を検索し、行番号を返します(-n)。一致が検出された場合(test -n ...)efgの最後の一致を検索します(すべてを検索し、最後をtail -n 1で取得します)。
z=$( grep -n efg $f | tail -n 1)
それ以外の場合は続行します。
結果は18:foofile.sh String alf="abc";
、「:」から行末まで切り取る必要があるようなものです。
((${z/:*/}-${a/:*/}))
2番目の式の最後の一致が最初の式の最初の一致を超えている場合、肯定的な結果を返します。
次に、ファイル名を報告しますecho $f
。
なぜ次のような単純なものではないのですか?
egrep -o 'abc|efg' $file | grep -A1 abc | grep efg | wc -l
0または正の整数を返します。
egrep -o(一致のみを表示、トリック:同じ行に複数の一致がある場合、それらが異なる行にあるかのように複数行の出力を生成します)
grep -A1 abc
(abcとその後ろの行を出力します)
grep efg | wc -l
(同じ行または後続の行でabcの後に見つかったefg行の0-nカウント。結果は「if」で使用できます)
パターンマッチングが必要な場合は、grepをegrepなどに変更できます。
探している2つの文字列「abc」と「efg」の間の距離に関する見積もりがある場合は、次のように使用できます。
grep -r . -e 'abc' -A num1 -B num2 | grep 'efg'
このようにして、最初のgrepは 'abc'とその後ろに#num1行、その後ろに#num2行を追加した行を返し、2番目のgrepはそれらすべてをふるいにかけて 'efg'を取得します。次に、それらが一緒に表示されるファイルを確認します。
これはうまくいくはずです:
cat FILE | egrep 'abc|efg'
一致するものが複数ある場合は、grep -vを使用して除外できます。