Если это переменная, вы можете сделать следующее.
find /path/to/src -type f -mmin -$((60 * $hourP)) -mmin +$((60*$hourN)) -exec pngquant --ext.png -v --force 256 {} \;
также лучше использовать +
вместо \;
для exec
оператора завершения, чтобы выполнить как pngquant a b c
, а неpngquant ;pngquant b; pngquant c
Для примера:
hourP=5
hourN=4
find /path/to/src -type f -mmin -$((60 * $hourP)) -mmin +$((60*$hourN)) -exec pngquant --ext.png -v --force 256 '{}' +
Использование GNU sed:
sed -E 's/\t0\>/&/3;t;d' file | wc -l
Как указал Исаак, если мы хотим посчитать ровно 3, сделайте это:
sed -n 's/\t0\>//4;t;s//&/3p' file | wc -l
Да, это можно сделать вawk
:
awk '{
k=0;
for(i=2;i<=NF;i++){
if($i == 0){
k++
}
}
if(k==3){
tot++
}
}
END{
print tot
}' file
А также с (GNU)sed
иwc
:
$ sed -nE '/\b0\b.*\b0\b.*\b0\b/p' file | wc -l
7
Но лично я бы вместо этого использовал perl:
$ perl -ale '$tot++ if (grep{$_ == 0 } @F) == 3 }{ print $tot' file
7
Или чуть менее сжатый:
$ perl -ale 'if( (grep{$_ == 0 } @F) == 3 ){
$tot++
}
END{
print $tot
}' file
7
И то же самое для игроков в гольф среди вас:
$ perl -ale '(grep{$_==0}@F)==3&&$t++}{print$t' file
7
-ale
:-a
заставляет perl вести себя как awk. Он прочитает каждую строку входного файла и разделит ее на пробелы в массив @F
. -l
добавляет \n
к каждому вызову print
и удаляет завершающие символы новой строки из ввода, а -e
— это сценарий, который следует применять к каждой строке ввода. $tot++ if (grep{$_ == 0 } @F) == 3
:увеличивает $tot
на единицу каждый раз, когда есть ровно 3 поля, равные 0
. Поскольку 1-е поле начинается с 1, мы знаем, что оно никогда не будет равно 0, поэтому нам не нужно его исключать. }{
:это просто сокращенный способ записи END{}
для указания блока кода, который будет выполняться после обработки файла. Итак, }{ print $tot
выведет общее количество строк ровно с тремя полями со значением 0
. С GNU grep
или ripgrep
$ LC_ALL=C grep -c $'\t''0\b.*\b0\b.*\b0\b' ip.txt
7
$ rg -c '\t0\b.*\b0\b.*\b0\b' ip.txt
7
где $'\t'
будет соответствовать символу табуляции, поэтому работает, даже если первый столбец 0
.
Пример запуска с большим файлом:
$ perl -0777 -ne 'print $_ x 1000000' ip.txt > f1
$ du -h f1
92M f1
$ time LC_ALL=C grep -c $'\t''0\b.*\b0\b.*\b0\b' f1 > f2
real 0m0.416s
$ time rg -c '\t0\b.*\b0\b.*\b0\b' f1 > f3
real 0m1.271s
$ time LC_ALL=C awk 'gsub(/\t0/,"")==3{c++} END{print c+0}' f1 > f4
real 0m8.645s
$ time perl -ale '$tot++ if (grep{$_ == 0 } @F) == 3 }{ print $tot' f1 > f5
real 0m14.349s
$ time LC_ALL=C sed -n 's/\t0\>//4;t;s//&/3p' f1 | wc -l > f6
real 0m14.075s
$ time LC_ALL=C sed -n 's/\t0\>/&/3p' f1 | wc -l > f8
real 0m6.772s
$ time LC_ALL=C awk '{
k=0;
for(i=2;i<=NF;i++){
if($i == 0){
k++
}
}
if(k==3){
tot++
}
}
END{
print tot
}' f1 > f7
real 0m10.675s
Удалите LC_ALL=C
, если файл может содержать не -символы ASCII. ripgrep
обычно быстрее, чем GNU grep
, но в тестовом прогоне GNU grep
был быстрее. Согласно автору ripgrep
, (?-u:\b)
можно использовать, чтобы избежать границы слова Unicode, но это привело к аналогичному времени для вышеуказанного случая.
Используйте Perl для подсчета количества строк, в которых ноль окружен слева символом TAB, а справа границей слова, три раза. В конце выведите количество таких строк.
perl -lne '$c += 3 == (() = /\t0\b/g)}{print $c' file
7
Другой способ сделать это — просмотреть поля:
perl -F'\t' -lane '$c++ if 3 == grep ! $_, @F[1..$#F]}{print $c' file
Еще один способ — использовать команду s///
в скалярном контексте:
perl -lne '$c += s/\t0\b//g == 3}{print $c' file
Для этого мы используем Gnu awk:
awk -F'\t' '
{
gsub(FS, FS FS)
$0 = $0 FS
if ($0 != gensub(FS"0"FS, "", 3, $0)) ++c
}
END{print c}
' file
Gnu grep также может вам помочь:
grep -cP '(.*\t0\b.*){3}' file
Можно сопоставить ровно n нулей (не более )с (GNU )sed:
$ i=3; # use the n value wanted.
$ sed -E 's/\<0\>/&/'"$i"';Ta;{s//&/'"$((i+1))"';T};:a;d' file | wc -l
7
Пояснение
i=3 # define the count of the regex to match.
sed -E # use extended regexes. reduce the need of `\`.
/\<0\>/ # match a zero (0) surrounded by whitespace.
s/\<0\>/&/ # replace zeros with themselves (detect that they exist)
'"$i"' # count how many zeros to change.
Ta # branch to label `a` (delete line) if
# there were less than `i` zeros
{s//&/'"$((i+1))"' # replace again but now i+1 zeros
T # branch to print if there are no more zeros than `i`.
:a;d # label to delete line (and catch any other line).
Эквивалент с (GNU )awk еще проще (gsub точно подсчитывает, сколько нулей было заменено ).
$ awk -vn=3 'gsub(/\<0\>/,"&")==3{c++}END{print c+0}' file
7
Другое awk
решение
awk '{x=$0; if(gsub(/\y0\y/, "", x) == 3) y++}; END{print y+0}' file
Это предполагает, что индексный столбец никогда не содержит 0
. Мы устанавливаем переменную x
в $0
, а затем заменяем 0
, окруженную границами слов, пустой строкой с помощью вызова gsub
. Поскольку gsub
возвращает количество сделанных замен, мы увеличиваем y
, если возвращаемое значение равно 3
$ grep -E '(\<0\>.*){3}' file | wc -l
7
Образец \<0\>
будет соответствовать одиночной цифре 0
. Полный шаблон соответствует любой строке, в которой есть не менее трех одиночных нулей.
Те подсчитывают только строки, которые содержат ровно три нуля, а не четыре, удаляют все строки, содержащие четыре или более нулей:
grep -E '(\<0\>.*){3}' file | grep -v -E '(\<0\>.*){4,}' | wc -l