在Unix shell中对一列数字求和

239

我有一个文件列表在files.txt中,我可以用以下方法获取它们的大小列表:

cat files.txt | xargs ls -l | cut -c 23-30

它会生成类似于这样的内容:

  151552
  319488
 1536000
  225280

如何获取所有这些数字的总和

24个回答

446
... | paste -sd+ - | bc

这是我找到的最短的一个(来自于UNIX命令行博客)。

编辑:感谢@Dogbert和@Owen,添加了-参数以实现可移植性。


9
谢谢伙计的添加,alias sum="paste -sd+ - | bc"已经加入到shell自动补全中。 - slf
如果您想全程使用Bash,请尝试以下程序代码:. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0)) - qneill
18
@slf,注意了,你刚刚过载了 /usr/bin/sum。请修复这个问题。 - qneill
3
注意,某些系统可能没有可用的bc!另一方面,我认为需要符合POSIX标准的awk - squirl
2
@donbright,请确保输入文件的每一行都只包含一个数字,没有其他内容。您可以通过省略 | bc 进行调试,并通过视觉检查输出来查找语法错误(它应该是 "a + b + c + ..." 的格式)。 - Todd Owen
显示剩余5条评论

170

这里是内容

cat files.txt | xargs ls -l | cut -c 23-30 | 
  awk '{total = total + $1}END{print total}'

39
使用awk是个不错的想法,但为什么要用cut呢?那是一个可预测的列数,所以可以使用... | xargs ls -l | awk '{total = total + $5}{END{print total}' - dmckee --- ex-moderator kitten
3
当然,你是正确的——只需将新内容附加到已有内容的末尾会更容易 :-) - Greg Reynolds
3
@dmckee的答案里有一个括号多了 :) - Dr. Jan-Philip Gehrcke
11
你可以使用total+=$1来代替total = total + $1,这样更简洁。 - squirl

11

如果文件名中有空格,那么cat命令将无法正常工作。以下是一个Perl单行命令解决方案。

perl -nle 'chomp; $x+=(stat($_))[7]; END{print $x}' files.txt

使用Perl的-l选项,chomp;是不必要的。-l会自动处理这个。 - undefined

11

不必使用cutls -l的输出中获取文件大小,可以直接使用:

$ cat files.txt | xargs ls -l | awk '{total += $5} END {print "Total:", total, "bytes"}'

Awk将"$5"解释为第五列。这是来自ls -l的列,给出了文件大小。


10
整个“ls -l”和“cut”命令有些复杂,当你拥有“stat”时就不需要了。它还容易受到“ls -l”的确切格式的影响(在更改“cut”列号之前,它并没有起作用)。
此外,修复了无用的cat使用
<files.txt xargs stat -c %s | paste -sd+ - | bc

3
哦,我用Unix已经有32年了,从未知道 <infile commandcommand <infile 是等价的,而且前者顺序更好。 - Camille Goudeseune

9
python3 -c"import os; print(sum(os.path.getsize(f) for f in open('files.txt').read().split()))"

或者,如果您只想求和这些数字,请使用管道运算符:

python3 -c"import sys; print(sum(int(x) for x in sys.stdin))"

1
当Python 2在今年年底消失时,... | python -c'import sys; print(sum(int(x) for x in sys.stdin))'将不再可用。 - Eponymous
don@oysters:~/Documents$ cat tax | python3 -c"import sys; print(sum(int(x) for x in sys.stdin))" Traceback (most recent call last): File "<string>", line 1, in <module> File "<string>", line 1, in <genexpr> ValueError: invalid literal for int() with base 10: '\n' - don bright

8

如果尚未安装bc,请尝试以下命令:

echo $(( $(... | paste -sd+ -) ))

替换为

... | paste -sd+ - | bc

$( ) <-- 执行命令并返回其值

$(( 1+2 )) <-- 返回计算结果

echo <-- 在屏幕上输出


6
cat files.txt | awk '{ total += $1} END {print total}'

你可以使用awk来完成相同的任务,它甚至可以跳过非整数。
$ cat files.txt
1
2.3
3.4
ew
1

$ cat files.txt | awk '{ total += $1} END {print total}'
7.7

或者您可以使用ls命令并计算人类可读的输出。
$ ls -l | awk '{ sum += $5} END  {hum[1024^3]="Gb"; hum[1024^2]="Mb"; hum[1024]="Kb"; for (x=1024^3; x>=1024; x/=1024) { if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x]; break; } } if (sum<1024) print "1kb"; }'
15.69 Mb

$ ls -l *.txt | awk '{ sum += $5} END  {hum[1024^3]="Gb"; hum[1024^2]="Mb"; hum[1024]="Kb"; for (x=1024^3; x>=1024; x/=1024) { if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x]; break; } } if (sum<1024) print "1kb"; }'
2.10 Mb

2
你甚至不需要管道符号:awk '{ total += $1} END {print total}' files.txt 更快。 - bmv

5

TMTWWTDI: Perl有一个文件大小操作符(-s)

perl -lne '$t+=-s;END{print $t}' files.txt

九年后...一些Perl高尔夫: perl -lne '$t+=-s}{print $t' files.txt - Kelly Setzer

5

如果你只想使用shell脚本而不需要awk或其他解释器,可以使用以下脚本:

#!/bin/bash

total=0

for number in `cat files.txt | xargs ls -l | cut -c 23-30`; do
   let total=$total+$number
done

echo $total

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接