Подсчитайте количество строк, в которых строка встречается n раз в нескольких столбцах.

Если это переменная, вы можете сделать следующее.

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 '{}' +
4
18.12.2019, 15:34
8 ответов

Использование GNU sed:

sed -E 's/\t0\>/&/3;t;d' file  | wc -l

Как указал Исаак, если мы хотим посчитать ровно 3, сделайте это:

sed -n 's/\t0\>//4;t;s//&/3p' file | wc -l
1
27.01.2020, 20:48

Да, это можно сделать в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.
5
27.01.2020, 20:48

С 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, но это привело к аналогичному времени для вышеуказанного случая.

2
27.01.2020, 20:48

Используйте 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
0
27.01.2020, 20:48
$ awk 'gsub(/\t0/,"")==3{c++} END{print c+0}' file
7
2
27.01.2020, 20:48

Можно сопоставить ровно 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
0
27.01.2020, 20:48

Другое awkрешение

awk '{x=$0; if(gsub(/\y0\y/, "", x) == 3) y++}; END{print y+0}' file

Это предполагает, что индексный столбец никогда не содержит 0. Мы устанавливаем переменную xв $0, а затем заменяем 0, окруженную границами слов, пустой строкой с помощью вызова gsub. Поскольку gsubвозвращает количество сделанных замен, мы увеличиваем y, если возвращаемое значение равно 3

0
27.01.2020, 20:48
$ grep -E '(\<0\>.*){3}' file | wc -l
       7

Образец \<0\>будет соответствовать одиночной цифре 0. Полный шаблон соответствует любой строке, в которой есть не менее трех одиночных нулей.

Те подсчитывают только строки, которые содержат ровно три нуля, а не четыре, удаляют все строки, содержащие четыре или более нулей:

grep -E '(\<0\>.*){3}' file | grep -v -E '(\<0\>.*){4,}' | wc -l
0
27.01.2020, 20:48

Теги

Похожие вопросы