2016-12-28 149 views
0

我输入文件如下减去值

100A 2000 
100B 150 
100C 800 
100A 1000 
100B 100 
100C 300 

我想减去第2列值在列中的每个uniq的值1 所以出来放应该像

100A 1000 
100B 50 
100C 500 

我已经试过

awk '{if(!a[$1])a[$1]=$2; else a[$1]=$2-a[$1]}END{ for(i in a)print i" " a[i]}' file 

但放出来是:

100A 0 
100B 0 
100C 0 

请指教

+0

如果第一列中的值重复多次,会发生什么? – ghoti

+0

在这种情况下,我想采取第一次和最后一次发生 – Vicky

+0

发布的解决方案都不会这样做,他们所做的只是为您提供的2入口示例(除了我的所有内容之外,不必要地将数据的一半保存在数组中然后以随机顺序产生输出,而不是严格按照键值读取的顺序进行输出,这可能或可能不合意)。 –

回答

2

这么多的(轻微)的变化同一主题。

awk ' 
    !($1 in a) {a[$1]=$2; next} 
    {a[$1]-=$2} 
    END {for (i in a) printf "%s %d\n",i,a[i]} 
' input.txt 

如果您愿意,可将其叠加为一行。

请记住,awk结构由多个condition { statement }对组成,因此您可以比使用if..else更优雅地表达您的需求。 (并不是说这里就是这种情况 - 这是一个足够简单的awk脚本,它可能并不重要,除非你是一个纯粹主义者。])

另外,要小心测试值的方式,已经在你的if的条件下完成了这个问题。请注意,a[$1]测试该数组索引处的值是否为非零值如果先前不存在,则会使索引存在空值。如果您想检查索引是否存在,请使用$1 in a


更新基于对您的问题评论...

如果你想减去从第一最后,忽略之间的,那么你需要保持记录你的第一个和你的最后一个。像这样的东西可能就足够了。

awk ' 
    !($1 in a){a[$1]=$2;next} 
    {b[$1]=$2} 
    END {for(i in b)if(i in a)print i,a[i]-b[i]} 
' input.txt 

请注意,正如埃德提到的,这会产生随机顺序的输出。如果你想要输出的顺序,你需要一个额外的数组来跟踪订单。例如,这将使用顺序的项目,首先看出:

awk ' 
    !($1 in a) { 
    a[$1]=$2; 
    o[++n]=$1; 
    next 
    } 
    { 
    b[$1]=$2 
    } 
    END { 
    for (n=1;n<=length(o);n++) 
     print o[n],a[o[n]]-b[o[n]] 
    } 
' i 

注意,所使用的length()函数来确定数组中元素的数目是不普遍之中AWK的方言,但它确实在两个工作gawk和one-true-awk(用于FreeBSD等)。

+0

我同意,但是当索引不存在时,我已经赋值$ [1] = $ 2,那么我的解决方案有什么问题? – Vicky

+0

您的测试是'if(!a [$ 1])',它不会测试不存在,它会导致索引存在(如果尚未存在)并测试值为零。我不知道为什么你会得到你在问题中提到的结果,但是你已经减少了你的减法。你从第一个减去第二个值,所以当我运行示例代码时,我会看到负数(1000 - 2000,100 - 150,300 - 800)。 – ghoti

+0

他们可能是负面的,因为我必须找到整数差异,我只是意识到我的解决方案也在工作,并且我的解决方案没有任何问题我只是用错误的输入文件运行它 – Vicky

1

这AWK的一行这项工作:

awk '{if($1 in a)a[$1]=a[$1]-$2;else a[$1]=$2} 
     END{for(x in a) print x, a[x]}' file 
+0

使用if/else而不是awk与'condition {statement}'构造提供的隐式结构有什么好处吗? – ghoti

+0

@ghoti我觉得if/else是明确的 – Kent

0

您可以使用此awk

awk 'a[$1]{a[$1]=a[$1]-$2; next} {a[$1]=$2} END{for(v in a){print v, a[v]}}' file 
1

在awk中。使用条件运算符值配售/减法,以保持紧:

$ awk '{ a[$1]+=($1 in a?-$2:$2) } END{ for(i in a)print i, a[i] }' file 
100A 1000 
100B 50 
100C 500 

解释:

{ 
    a[$1]+=($1 in a?-$2:$2) # if $1 in a already, subtract from it 
           # otherwise add value to it 
} 
END { 
    for(i in a)    # go thru all a 
     print i, a[i]   # and print keys and values 
} 
1

给你提供的样品输入,所有你需要的是:

$ awk '$1 in a{print $1, a[$1]-$2} {a[$1]=$2}' file 
100A 1000 
100B 50 
100C 500 

如果是这样的不是所有你需要的,然后提供更真实的代表性样本输入/输出,包括那些不够好的情况。

+0

感谢埃德工作,这是一种方式来实现,如果-else在awk的条件块中执行{print $ 1,a [$ 1] - $ 2},当$ 1中a评估为true且{a [$ 1] = $ 2}当其他人执行1美元时评估为假? – Vicky

+0

不,因为还有其他的东西,你可以在第一个'}之前插入'; next'。现在分配发生在每一行,它只是在功能上无关紧要。 –