获取零的长度(由中断)

时间:2015-02-09 16:52:43

标签: bash awk

我有一长串的零和零:

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

我可以轻松获得1之间的平均零数(只有总数/ 1):

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

但是如何在两者之间获得零串的长度呢?在上面的简短示例中,它将是:

3
5
5
2

14 个答案:

答案 0 :(得分:17)

我要包含uniq以便更轻松地阅读:

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

答案 1 :(得分:10)

编辑:已修复最后一行为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 :(得分:7)

使用awk,我会使用值为0的字段评估为False的事实:

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

返回:

3
5
5
2

另外,请注意END块以打印任何&#34;剩余的&#34;在最后1之后出现的零。

解释

  • !$1{s++; next}如果字段不为True,即字段为0,则递增计数器。然后,跳到下一行。
  • {if (s) print s; s=0}否则,打印计数器的值并重置它,但它只包含一些值(如果文件以0开头,则为了避免打印1。 / LI>
  • END {if (s) print s}在处理完文件后打印计数器的剩余值,但前面没有打印过。

答案 3 :(得分:5)

如果你的file.txt 只是一列1和0,你可以使用awk并将记录分隔符更改为&#34; 1 \ n&#34;。这使得每个&#34;记录&#34;一个&#34; 0 \ n&#34;的序列,记录中0的计数是记录的长度除以2.对于前导和尾随的1和0,计数是正确的。

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

答案 4 :(得分: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

答案 5 :(得分:3)

您可以使用awk

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

<强>解释

特殊变量$1包含一行文本的第一个字段)的值。除非您使用-F命令行选项指定字段分隔符,否则默认为宽空格 - 意味着$1在您的示例中将包含01

如果$1的值等于0,则名为s的变量会增加,但如果$1等于1,则s的当前值1}}被打印(如果大于零)并重新初始化为0。 (请注意,awk在第一次递增操作之前用s初始化0

在处理完最后一行输入后执行END块。如果文件以0(s)结尾,则会打印文件结尾与最后0之间1的数量。 (没有END块,他们就不会打印)

<强>输出

3
5
5
2

答案 6 :(得分:3)

如果您可以使用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

答案 7 :(得分:3)

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.

答案 8 :(得分:3)

另一种方式:

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

&#34;重置&#34; 1时的行计数器。

打印

3
5
5
2

答案 9 :(得分:2)

我的尝试。不是那么漂亮但是......:3

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

输出:

3
5
5
2

答案 10 :(得分:2)

最简单的解决方案是将sedawk一起使用,如下所示:

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

答案 11 :(得分:2)

一个有趣的,纯粹的Bash:

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

这告诉read使用1作为分隔符,即在遇到1时立即停止阅读; read将0存储在数组u的字段中。然后,我们只需要使用u计算${#u[@]}中的字段数。 || ((${#u[@]}))就是为了防止您的文件以1结尾。

答案 12 :(得分:2)

更奇怪(而且不完全正确)的方式:

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

打印

3
5
5
2
0

  • 读取文件时,记录分隔符设置为1
  • 字符-0x31
  • with autosplit -a(将记录拆分为数组@F
  • 并打印@F中的元素数量,例如say @F+0或可以使用say scalar @F

不幸的是,在最后的1(作为记录分隔符)后,它会打印一个空记录 - 因此会打印最后一个0

这是不正确的解决方案,仅作为替代好奇心显示出来。

答案 13 :(得分:2)

扩展erickson's excellent answer,您可以说:

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

man uniq我们看到uniq的目的是:

  

从INPUT(或标准输入)过滤相邻的匹配线,写入   输出(或标准输出)。

所以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,则打印第二个字段。