次の内容を含むファイルがあります。
$ cat compromised_header.txt
some unique string 1
some other unique string 2
another unique string 3
上記のファイルのすべての行がまったく同じ順序であり、行の間に中間行がないすべてのファイルを見つけたいと思います。
入力ファイルの例:
$ cat a-compromised-file.txt
some unique string 1
some other unique string 2
another unique string 3
unrelated line x
unrelated line y
unrelated line z
私は以下を試しましたgrep
。
grep -rlf compromised_header.txt dir/
しかし、次のように一致するので、予想されるファイルを提供しているかどうかはわかりません。
some unique string 1
unrelated line x
unrelated line y
unrelated line z
答え1
以下をサポートするawkを使用してくださいnextfile
。
NR == FNR {
a[++n]=$0; next
}
$0 != a[c+1] && (--c || $0!=a[c+1]) {
c=0; next
}
++c >= n {
print FILENAME; c=0; nextfile
}
再帰の場合find
:
find dir -type f -exec gawk -f above.awk compromised_header.txt {} +
または、次のように機能することもできます。
pcregrep -rxlM "$( perl -lpe '$_=quotemeta' compromised_header.txt )" dir
pcregrep--fixed-strings
は--multiline
。
Perlがフルルックモードの場合(メモリに収まらないほど大きなファイルには適していません):
find dir -type f -exec perl -n0777E 'BEGIN {$f=<>} say $ARGV if /^\Q$f/m
' compromised_header.txt {} +
答え2
grep
1行だけを一致させるよりも強力なものを使用する必要があります。
perl
この種の操作に最適な複数行マッチングを実行し、それらを組み合わせてfind
検索するファイルのリストを生成できます。
find dir/ -type f -iname '*.txt' -exec perl -e '
local $/; # slurp in entire files, instead of one line at a time
my $firstfile = shift @ARGV; # get name of the first file
open(F,"<",$firstfile) or die "Error opening $firstfile: $!";
my $first = <F>; # read it in
close(F);
my $search = qr/\Q$first\E/; # compile to a fixed-string RE
# now read in remaining files and see if they match
while(<>) {
next if ($ARGV eq $firstfile);
if (m/$search/m) {
print $ARGV,"\n";
};
}' ./compromised_header.txt {} +
dir/
これにより、最初のファイル( "compromised_header.txt")の正確なテキストを含むすべての* .txtファイルのファイル名が印刷されます。
メモ:
演算子は
qr//
正規表現をコンパイルします。主な用途は、ループで使用する前にREをプリコンパイルして、すべてのループが再コンパイルされないようにすることで、時間とCPUサイクルを無駄にすることです。ジョブで使用されている
\Q
固定文字列として解釈されるように設計されたREモードでのテキストの開始と終了を表示します。つまり、文字列に存在できるすべてのメタ文字は、特別な意味を無効にするために引用されます。 「引用メタ文字」を確認して検索し、詳細をご覧ください。\E
qr//
man perlre
perldoc -f quotemeta
見苦しくて複雑で読みにくい1行のスクリプトのように見える場合は、次のことをスタンドアロンスクリプトで試してください。
#!/usr/bin/perl
local $/; # slurp in entire files, instead of one line at a time
my $firstfile = shift @ARGV; # get name of the first file
open(F,"<",$firstfile) or die "Error opening $firstfile: $!";
my $first = <F>; # read it in
close(F);
my $search = qr/\Q$first\E/; # compile to a fixed-string RE
# now read in remaining files and see if they match
while(<>) {
next if ($ARGV eq $firstfile);
if (m/$search/m) {
print $ARGV,"\n";
};
}
たとえば、別の名前で保存しcheck.pl
て実行可能にしますchmod +x check.pl
。次に、次を実行します。
find dir/ -type f -iname '*.txt' \
-exec ./check.pl ./compromised_header.txt {} +
答え3
PCRE -PモードでGNU grepを使用している場合は、slurpモード-zと再帰的に-r list -l正規表現$ reに一致するファイルを操作できます。正規表現は参照ヘッダファイルから作成され、Perl正規表現コンテキストですべての特殊文字をエスケープします。
re=$(< compromised_header.txt perl -lpe '$_=quotemeta')
re=${re//[${IFS#??}]/\\n}
grep -lrzP "(?m)^$re" .
答え4
検索文字列に複数の末尾の改行またはASCII NUL文字がないとします(参照ファイルをシェル変数として読み込むときのトラップ詳細は)以下を使用できます。リップグレップ:
rg -lUF "$(< compromised_header.txt)" dir/
-F
ファイルの内容を正規表現で処理せずに文字通り検索するには、オプションを使用します。
-U
複数行検索を有効にするオプション
rg
デフォルトでは再帰的に検索されますが、デフォルトではスマートフィルタリングも実行します(.gitignore
ルールの遵守、隠しファイル/フォルダの無視、バイナリの無視など)。-uuu
のように動作させるには使用しますgrep -r
。
私のブログ投稿をご覧くださいcliツールを使用した複数行固定文字列の検索と置換このようなより多くの行の作業の場合。