我有这个文件:
1 2
2 7
3 4
4 7
5 3
6 7
7 1
8 2
9 4
我想要的输出是
1 13
2 17
3 7
在我的输入中,我有 9 行,我想将其减少到三行,同时保留第二列的总量。例如,第一列中的 1 代表第二列中的 1,2,3 和 13,第一行代表加法 (2+4+7) 等等。有什么想法吗?可以使用 awk/perl 或任何其他 Linux 工具。
答案1
这是一个awk
解决方案:
awk '{ s+=$2; if (!(NR%3)) { k++; print k,s; s=0 } };
END { if (NR%3) { k++; print k, s } }' file.txt
它忽略第一列,更愿意将其生成为k
输出行号。第二列在 中求和s
,并且每三行 ( (NR % 3) == 0
) 输出它并重置累加器。最后,如果我们有任何剩余的行,我们将输出剩余的总和。
示例文件的输出
1 13
2 17
3 7
只是为了完整起见,这里有一个干燥使用函数处理 modulo-3 和 END 块中的重复代码的版本:
awk 'function outsum() { print ++k,s; s=0 };
{ s+=$2; if (!(NR%3)) { outsum() } };
END { if (NR%3) { outsum() } }' file.txt
答案2
Perl解决方案:
perl -lane '
$s += $F[1];
print(join "\t", ++$l, $s), $s = 0
if 0 == $. % 3 || eof;
' input-file
-n
逐行读取输入-a
将空白处的每一行拆分到 @F 数组中$s
用作保持总和的变量$.
是一个特殊变量,包含输入行号$l
是输出行号
答案3
这也许应该去代码高尔夫。这是一个内衬没有 perl
,awk
或者sed
:
paste <(for i in $(seq 1 0.33333333334 $(A=$(wc -l input.dat | cut -d ' ' -f 1); echo $A/3+1 | bc)); do echo $i/1 | bc; done) <(tr -s ' ' < input.dat | cut -d ' ' -f 3) | datamash -g 1 sum 2
详细
左侧
for i in $(seq 1 0.33333333334 $(A=$(wc -l input.dat | cut -d ' ' -f 1); echo $A/3+1 | bc)); do echo $i/1 | bc; done
生成一个类似的列表(它占输入文件中的实际行数):
1
1
1
2
2
2
3
3
3
还有右边
tr -s ' ' < input.dat | cut -d ' ' -f 3
截断输入文件的第一列,留下:
2
7
4
7
3
7
1
2
4
paste
将它们组合回来并datamash
是否通过...分组。
答案4
这是仅使用 shell 命令的版本。我把它分成几行,但你没有理由不能把它作为一行行一起滚动(这就是它的开始方式):
(
s=0 k=1 n=0
while read x v
do
s=$((s+v)) n=$((n+1))
if [[ n -eq 3 ]]
then
echo $k $s
k=$((k+1)) n=0 s=0
fi
done
[[ s -gt 0 ]] && echo $k $s
) <file.txt
( s=0 k=1 n=0; while read x v; do s=$((s+v)) n=$((n+1)); if [[ n -eq 3 ]]; then echo $k $s; k=$((k+1)) n=0 s=0; fi; done; [[ s -gt 0 ]] && echo $k $s ) <file.txt