Получите длины нулей (прерванных одними)

У меня длинный столбец единиц и нулей:

0
0
0
1
0
0
0
0
0
1
0
0
0
0
0
1
0
0
1
....

Я могу легко получить среднее число нулей между ними (всего общее/одно):

ones=$(grep -c 1 file.txt)
lines=$(wc -l < file.txt)
echo "$lines / $ones" | bc -l

Но как я могу получить длину строк нулей между ними? В кратком примере выше это будет:

3
5
5
2

Ответы

Ответ 1

Изменить: исправлено для случая, когда последняя строка равна 0

Простой в awk:

awk '/1/{print NR-prev-1; prev=NR;}END{if (NR>prev)print NR-prev;}'

Не так сложно в bash, либо:

i=0
for x in $(<file.txt); do
  if ((x)); then echo $i; i=0; else ((++i)); fi
done
((i)) && echo $i 

Ответ 2

Я бы включил uniq для более легко читаемого подхода:

uniq -c file.txt | awk '/ 0$/ {print $1}'

Ответ 3

Используя awk, я бы использовал тот факт, что поле со значением 0 оценивается как False:

awk '!$1{s++; next} {if (s) print s; s=0} END {if (s) print s}' file

Это возвращает:

3
5
5
2

Также обратите внимание на блок END для печати любых "оставшихся" нулей, появляющихся после последнего 1.

Объяснение

  • !$1{s++; next} Если поле не True, то есть, если поле 0, увеличьте счетчик. Затем перейдите к следующей строке.
  • {if (s) print s; s=0} в противном случае напечатайте значение счетчика и reset его, но только если оно содержит некоторое значение (чтобы избежать печати 0, если файл начинается с 1).
  • END {if (s) print s} напечатайте оставшееся значение счетчика после обработки файла, но только если оно не было напечатано ранее.

Ответ 4

Если файл file.txt - это только столбец из них и нули, вы можете использовать awk и изменить разделитель записи на "1\n". Это делает каждую "запись" последовательностью "0\n", а счетчик 0 в записи - это длина записи, деленная на 2. Подсчет будет правильным для начальных и конечных и нулей.

awk 'BEGIN {RS="1\n"} { print length/2 }' file.txt

Ответ 5

Сегодня это довольно популярный вопрос. Присоединившись к партии поздно, вот еще одна короткая команда gnu-awk, чтобы выполнить эту работу:

awk -F '\n' -v RS='(1\n)+' 'NF{print NF-1}' file
3
5
5
2

Как это работает:

-F '\n'           # set input field separator as \n (newline)
-v RS='(1\n)+'    # set input record separator as multipled of 1 followed by newline
NF                # execute the block if minimum one field is found
print NF-1        # print num of field -1 to get count of 0

Ответ 6

Вы можете использовать awk:

awk '$1=="0"{s++} $1=="1"{if(s)print s;s=0} END{if(s)print(s)}'

Объяснение:

Специальная переменная $1 содержит значение первого поля (столбца) строки текста. Если вы не укажете разделитель поля с помощью параметра командной строки -F, он по умолчанию будет иметь значение wide - значение $1 будет содержать 0 или 1 в вашем примере.

Если значение $1 равно 0, переменная с именем s будет увеличиваться, но если $1 равна 1, то текущее значение s будет напечатано (если больше нуля) и повторно инициализируется до 0. (Обратите внимание, что awk инициализирует s с помощью 0 до первой операции приращения)

Блок END запускается после обработки последней строки ввода. Если файл заканчивается на 0 (s), будет напечатан номер 0 между концом файла и последним 1. (Без блока END они не будут печататься)

Выход

3
5
5
2

Ответ 7

если вы можете использовать perl:

perl -lne 'BEGIN{$counter=0;} if ($_ == 1){ print $counter; $counter=0; next} $counter++' file
3
5
5
2

На самом деле он выглядит лучше с помощью awk той же логики:

awk '$1{print c; c=0} !$1{c++}' file 
3
5
5
2

Ответ 8

Pure bash:

sum=0
while read n ; do
    if ((n)) ; then
        echo $sum
        sum=0
    else
        ((++sum))
    fi
done < file.txt
((sum)) && echo $sum # Don't forget to output the last number if the file ended in 0.

Ответ 9

Другой способ:

perl -lnE 'if(m/1/){say $.-1;$.=0}' < file

"reset" счетчик строк, если 1.

печатает

3
5
5
2

Ответ 10

Моя попытка. Не так красиво, но..: 3

grep -n 1 test.txt | gawk '{y=$1-x; print y-1; x=$1}' FS=":"

Из:

3
5
5
2

Ответ 11

Простейшим решением было бы использовать sed вместе с awk, например:

sed -n '$bp;/0/{:r;N;/0$/{h;br}};/1/{x;bp};:p;/.\+/{s/\n//g;p}' input.txt \
  | awk '{print length}'

Объяснение:

Команда sed разделяет 0 и создает вывод следующим образом:

000
00000
00000
00

Входящий в awk '{print length}' вы можете получить счетчик 0 для каждого интервала:

Вывод:

3
5
5
2

Ответ 12

Смешной, в чистом Bash:

while read -d 1 -a u || ((${#u[@]})); do
    echo "${#u[@]}"
done < file

Это говорит read использовать 1 как разделитель, т.е. прекратить чтение, как только встретится 1; read хранит 0 в полях массива u. Тогда нам нужно только подсчитать количество полей в u с помощью ${#u[@]}. || ((${#u[@]})) здесь на всякий случай, если ваш файл не заканчивается символом 1.

Ответ 13

Более странный (и не полностью правильный) способ:

perl -0x31 -laE 'say @F+0' <file

печатает

3
5
5
2
0

Это

  • читает файл с разделителем записей, устанавливается на символ 1 -0x31
  • с autosplit -a (разбивает запись на массив @F)
  • и печатает количество элементов в @F, например. say @F+0 или может использовать say scalar @F

К сожалению, после окончательного 1 (как разделитель записей) он печатает пустую запись - поэтому печатает последний 0.

Это неправильное решение, показывающее его только как альтернативное любопытство.

Ответ 14

Расширяя отличный ответ erickson, вы можете сказать:

$ uniq -c file | awk '!$2 {print $1}'
3
5
5
2

Из man uniq мы видим, что цель uniq заключается в следующем:

Отфильтровать соседние строки соответствия из INPUT (или стандартного ввода), написание к OUTPUT (или к стандартному выходу).

So uniq группирует числа. Используя опцию -c, мы получаем префикс с числом вхождений:

$ uniq -c file
      3 0
      1 1
      5 0
      1 1
      5 0
      1 1
      2 0
      1 1

Затем нужно напечатать те счетчики перед 0. Для этого мы можем использовать awk как: awk '!$2 {print $1}'. То есть: напечатайте второе поле, если поле 0.