"abc"と "efg"をこの順序で含むファイルを見つけたいのですが、この2つの文字列はそのファイルの異なる行にあります。例:内容を含むファイル:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
一致するはずです。
Grepはこの操作には不十分です。
pcregrep 最近のほとんどのLinuxシステムに見られるものは、
pcregrep -M 'abc.*(\n|.)*efg' test.txt
-M、 - multilineを使用すると、パターンを複数行に一致させることができます。
新しい pcre2grep もあります。どちらも PCREプロジェクト によって提供されています。
pcre2grepは、Mac OS Xで Mac Ports からpcre2
の一部として利用可能です:
% Sudo port install pcre2
そしてvia 自作 として
% brew install pcre
またはpcre2用
% brew install pcre2
Grepでそれが可能かどうかはわかりませんが、sedによって非常に簡単になります。
sed -e '/abc/,/efg/!d' [file-with-content]
これは この答え に触発された解決策です。
'abc'と 'efg'を同じ行に入れることができる場合
grep -zl 'abc.*efg' <your list of files>
'abc'と 'efg'が別々の行になければならない場合:
grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
Params:
-z
入力を一連の行として扱い、各行は改行ではなくゼロバイトで終了します。すなわち、grepは入力を1つの大きな線として脅かします。
-l
出力が通常印刷される各入力ファイルの名前。
(?s)
はPCRE_DOTALLをアクティブにします。これは「。」を意味します。文字または改行を見つけます。
sedはLJが上記のように十分であるべきです、
!dの代わりに、単にpを使って印刷することができます。
sed -n '/abc/,/efg/p' file
私は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 'abc.*def' | tr '\a' '\n'
ここでは、改行の代わりに\a
(ASCII 7)というアラーム文字を使用しています。これはあなたのテキストにはほとんど見られず、grep
はそれを.
と突き合わせることも、\a
と突き合わせることもできます。
あなたが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タグを追加してください。また、これは厳密に例を示した方法に準拠していますが、abcがefgの後に2回目に表示されたときにはまったく機能しません。それを処理したい場合は、/ abc /の場合などにifを追加してください。
ワンライナーをawk:
awk '/abc/,/efg/' [file-with-content]
私は数日前に、複数行のマッチングや条件を使って直接これをサポートする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 でより多くの情報を見つけることができます。
残念ながら、できません。 grep
のドキュメントから:
grepは、名前付き入力ファイル(ファイル名がない場合、または単一のハイフンマイナス( - )がファイル名として指定されている場合は標準入力)から、指定したパターンとの一致を含むlinesを検索します。
両方の単語を互いに接近させる必要がある場合、たとえば3行以内であれば、これを実行できます。
find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
同じ例ですが* .txtファイルのみをフィルタリングします。
find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
また、正規表現で検索したい場合は、grep
コマンドをegrep
コマンドに置き換えることもできます。
あなたがコンテキストを使用しても構わないと思っているのであれば、これは次のように入力することで達成できます。
grep -A 500 abc test.txt | grep -B 500 efg
これは、お互いの500行以内にある限り、すべてのbetween "abc"と "efg"を表示します。
Sedオプションが最も簡単で簡単ですが、LJのワンライナーは残念ながら最もポータブルではありません。 Cシェルのバージョンで立ち往生しているものは、彼らの強打を脱出する必要があります。
sed -e '/abc/,/efg/\!d' [file]
残念ながらこれはbash et al。ではうまくいきません。
あなたがパターンのシーケンスに熱心でない場合は、grepを使うことができます。
grep -l "pattern1" filepattern*.* | xargs grep "pattern2"
例
grep -l "vector" *.cpp | xargs grep "map"
grep -l
は最初のパターンにマッチするすべてのファイルを見つけ、xargsは2番目のパターンをgrepします。お役に立てれば。
#!/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の-Pオプションを使用して、マルチファスタファイルからファスタシーケンスを抽出するためにこれを使用しました。
grep -Pzo ">tig00000034[^>]+" file.fasta > desired_sequence.fasta
-P Perlベースの検索では-P改行char -oではなく0バイトで行末を作るための-z grepは行全体を返すので(この場合は-zがファイル全体であるため)一致したものだけをキャプチャします。正規表現の中核は[^>]
で、これは「シンボル以下」と解釈されます。
ファイルパターン*.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
を報告します。
あなたが探している2つの文字列 'abc'と 'efg'の間の距離についていくらかの見積もりがあるなら、あなたは使用するかもしれません:
grep -r -e 'abc' -A num1 -B num2 | grep 'efg'
このように、最初のgrepは 'abc'とそれに続く#num1行、その後に#num2行の行を返し、2番目のgrepはそれらのすべてを見て '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
を黙らせ、一致が見つかった場合はただちに戻ります。終了コードだけに関心があるためです。これでうまくいくでしょうか。
Perl -lpne 'print $ARGV if /abc.*?efg/s' file_list
$ARGV
は、file_list /s
修飾子からの読み込みが改行を越えて検索されるときの現在のファイルの名前を含みます。