4

我有这样的文件

文件.txt

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每当第二列中的字段值$2发生更改时,我希望使用 awk 或 bash 脚本将第一列中的行号计数重置为 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
.
.
. 
4

4 回答 4

9

只要你不介意内存使用过多,并且第二列是排序的,我认为这是最好玩的:

awk '{$1=a[$2]+++0;print}' input.txt
于 2012-10-23T01:14:49.137 回答
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-- 这是“打印行”的 awk 简写。这确实是一个总是评估为“真”的条件,当一个条件/语句对缺少一个语句时,该语句默认为“打印”。

非常基本,真的。

当然,一个问题是,当您更改 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

盐调味。

于 2012-10-22T23:41:16.870 回答
2

一个纯解决方案:

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"

该解决方案(恕我直言)具有使用可读算法的优势。我喜欢其他人给出的答案,但这对初学者来说并不全面

注意

((...))是一个算术命令,如果表达式非零,则返回退出状态 0,如果表达式为零,则返回 1。let如果需要副作用(分配),也用作 的同义词。见http://mywiki.wooledge.org/ArithmeticExpression

于 2012-10-22T23:45:16.287 回答
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行工作需要条件。

于 2012-10-22T23:14:53.533 回答