カウントと比較を同時に行うコマンドを使用するだけで、中間ステップを回避できます。
find . -type f -exec perl -nle 'END { print $ARGV if $h{"{"} != $h{"}"} } $h{$_}++ for /([}{])/g' {}\;
これにより、Perl プログラムがファイルごとに 1 回呼び出されます。Perl プログラムは、各タイプの中括弧の数をカウントし、カウントが一致しない場合はファイルの名前を出力します。
/([}{]])/
セクションに注意する必要がfind
あり{}
ます/([{}]])/
。
警告: このコードをソース コードに対して実行しようとすると、誤検出と誤検出が発生します。次のケースを考慮してください。
バランスがとれていますが、文字列のカーリー:
if ($s eq '{') {
print "I saw a {\n"
}
アンバランスですが、文字列のカーリー:
while (1) {
print "}";
B::Deparseを使用して Perl コマンドを展開できます。
perl -MO=Deparse -nle 'END { print $ARGV if $h{"{"} != $h{"}"} } $h{$_}++ for /([}{])/g'
結果は次のとおりです。
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
sub END {
print $ARGV if $h{'{'} != $h{'}'};
}
;
++$h{$_} foreach (/([}{])/g);
}
プログラムの各部分を見てみましょう。
BEGIN { $/ = "\n"; $\ = "\n"; }
これは-l
オプションが原因です。入力と出力の両方のレコード セパレータを "\n" に設定します。これは、読み込まれたものはすべて "\n" ベースのレコードに分割され、すべての print ステートメントには "\n" が追加されることを意味します。
LINE: while (defined($_ = <ARGV>)) {
}
これは-n
オプションによって作成されます。コマンドライン (ファイルが渡されない場合は STDIN) を介して渡されたすべてのファイルをループし、それらのファイルの各行を読み取ります。これは$ARGV
、 によって最後に読み取られたファイルにも設定され<ARGV>
ます。
chomp $_;
$/
これにより、読み取られたばかりの行から変数にあるものはすべて削除されます( $_
)。ここでは何も役に立ちません。-l
オプションが原因でした。
sub END {
print $ARGV if $h{'{'} != $h{'}'};
}
これは END ブロックです。このコードはプログラムの最後に実行されます。キーに関連付けられた と に格納されている値が等しい場合、(最後に読み取ら$ARGV
れたファイルの名前。上記を参照) が表示されます。%h
'{'
'}'
++$h{$_} foreach (/([}{])/g);
これはさらに分解する必要があります。
/
( #begin capture
[}{] #match any of the '}' or '{' characters
) #end capture
/gx
一致する文字列に含まれる「{」および「}」文字のリストを返す正規表現です。文字列が指定されていないため、$_
変数 (ファイルから最後に読み取られた行を保持します。上記を参照) と照合されます。そのリストは、リストforeach
内の各項目 (したがって名前) に対して前にあるステートメントを実行するステートメントに入力されます。また、 ( Perl では一般的な変数である$_
ことがわかるように) リストの項目になるように設定します。$_
++h{$_}
この行は、関連付けられている $h の値$_
('{' または '}' のいずれか、上記を参照) を 1 増やします。