Вы можете избежать промежуточных шагов, просто используя команду, которая выполняет подсчет и сравнение одновременно:
find . -type f -exec perl -nle 'END { print $ARGV if $h{"{"} != $h{"}"} } $h{$_}++ for /([}{])/g' {}\;
Это вызывает Perl-программу один раз для каждого файла, Perl-программа подсчитывает количество фигурных скобок каждого типа и печатает имя файла, если их количество не совпадает.
Вы должны быть осторожны с разделом /([}{]])/
, find
подумает, что нужно сделать замену на {}
, если вы скажете /([{}]])/
.
ВНИМАНИЕ: этот код будет содержать ложные и отрицательные результаты, если вы пытаетесь запустить его с исходным кодом. Рассмотрим следующие случаи:
сбалансировано, но фигурные скобки в строках:
if ($s eq '{') {
print "I saw a {\n"
}
несбалансированный, но вьющиеся строки:
while (1) {
print "}";
Вы можете развернуть команду Perl, используя B :: Deparse :
perl -MO = Deparse -nle 'END {напечатать $ ARGV, если $ h {"{"}! = $ H {"}"}} $ h {$ _} ++ для / ([} {]) / г '
Что приводит к:
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
sub END {
print $ARGV if $h{'{'} != $h{'}'};
}
;
++$h{$_} foreach (/([}{])/g);
}
Теперь мы можем взглянуть на каждый фрагмент программы:
BEGIN { $/ = "\n"; $\ = "\n"; }
Это вызвано параметром -l
. Он устанавливает разделители входных и выходных записей в «\ n». Это означает, что все прочитанное будет разбито на записи на основе "\ n", и к любому оператору печати будет добавлено "\ 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, связанное с $_
(которое будет или '{' или '}', см. Выше), на единицу.