我有一个像这样的文件
0 1 a
1 1 b
2 1 d
3 1 d
4 2 g
5 2 a
6 3 b
7 3 d
8 4 d
9 5 g
10 5 g
.
.
.
我希望每当第二列$1
中的字段值使用awk或bash脚本更改时,第一列$2
中的重置行数会计为0。
0 1 a
1 1 b
2 1 d
3 1 d
0 2 g
1 2 a
0 3 b
1 3 d
0 4 d
0 5 g
1 5 g
.
.
.
答案 0 :(得分:8)
只要你不介意一点多余的内存使用量,并且第二列是排序的,我认为这是最有趣的:
awk '{$1=a[$2]+++0;print}' input.txt
答案 1 :(得分:6)
这个awk单行似乎对我有用:
[ghoti@pc ~]$ awk 'prev!=$2{first=0;prev=$2} {$1=first;first++} 1' input.txt
0 1 a
1 1 b
2 1 d
3 1 d
0 2 g
1 2 a
0 3 b
1 3 d
0 4 d
0 5 g
1 5 g
让我们分开脚本,看看它的作用。
prev!=$2 {first=0;prev=$2}
- 这就是重置你的计数器的原因。由于prev
的初始状态为空,我们重置第一行输入,这很好。{$1=first;first++}
- 对于每一行,设置第一个字段,然后增加我们用来设置第一个字段的变量。1
- 这是“打印线”的简写。它实际上是一个总是求值为“true”的条件,当条件/语句对缺少一个语句时,该语句默认为“print”。非常基本,真的。
当然,一个问题是当你更改awk中任何字段的值时,它会使用设置的任何字段分隔符重写该行,默认情况下它只是一个空格。如果要调整此值,可以设置OFS
变量:
[ghoti@pc ~]$ awk -vOFS=" " 'p!=$2{f=0;p=$2}{$1=f;f++}1' input.txt | head -2
0 1 a
1 1 b
盐味。
答案 2 :(得分:2)
纯bash解决方案:
file="/PATH/TO/YOUR/OWN/INPUT/FILE"
count=0
old_trigger=0
while read a b c; do
if ((b == old_trigger)); then
echo "$((count++)) $b $c"
else
count=0
echo "$((count++)) $b $c"
old_trigger=$b
fi
done < "$file"
该解决方案(IMHO)具有使用可读算法的优点。我喜欢其他人给出的答案,但这并不是 对初学者来说很全面。
注意强>:
((...))
是一个算术命令,如果表达式非零,则返回退出状态0;如果表达式为零,则返回1。如果需要副作用(赋值),也用作let
的同义词。见http://mywiki.wooledge.org/ArithmeticExpression
答案 3 :(得分:0)
Perl解决方案:
perl -naE '
$dec = $F[0] if defined $old and $F[1] != $old;
$F[0] -= $dec;
$old = $F[1];
say join "\t", @F[0,1,2];'
每次从第一列中减去 $dec
。当第二列更改(其先前值存储在$old
中)时,$dec
会增加以将第一列再次设置为零。第一行工作需要defined
条件。