Unixシェルで数値の列を合計する


198

内のファイルのリストを指定するとfiles.txt、次のようなサイズのリストを取得できます。

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

これは次のようなものを生成します:

  151552
  319488
 1536000
  225280

これらの数値の合計を取得するにはどうすればよいですか?

回答:


383
... | paste -sd+ - | bc

私が見つけた最も短いものです(UNIXコマンドラインのブログから)。

編集:- @Dogbertと@Owenに感謝し、移植性の引数を追加しました。


いいね。ラストが必要-Solarisでも
Owen B

8
alias sum="paste -sd+ - | bc"シェルの完了に追加、おかげで
slf '30

. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))常にすべてのbashが必要な場合:
qneill 2015

13
@slf、要注意、オーバーロード/usr/bin/sum
qneill 2015

3
注意してください、bcいくつかのシステムでは利用できません!awk一方、POSIXコンプライアンスには(私は信じています)が必要です。
vktec 2017年

154

さあ

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

34
awkを使用するのは良い考えですが、なぜcut?これは予測可能な列番号なので、使用します... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee ---元モデレーターの子猫

3
あなたはもちろん正しいです-すでにそこにあったものの最後に追加する方が簡単でした:-)
グレッグ・レイノルズ

2
@dmckeeの回答の1つの括弧が多すぎます:)
Jan-Philip Gehrcke博士2013

7
これを少し短くするには、次のtotal+=$1代わりに使用できますtotal = total + $1
vktec

10

ls -lの出力からファイルサイズを取得するためにカットを使用する代わりに、直接使用できます。

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

Awkは「$ 5」を5番目の列として解釈します。これは、ファイルサイズを示すls -lの列です。


10

ファイル名にスペースが含まれている場合、catは機能しません。代わりにここにperlのワンライナーがあります。

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

8
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 -c'import sys; print(sum(int(x) for x in sys.stdin))'python 2が今年の終わりに姿を消したとき。
町の名を冠した

don @ oysters:〜/ Documents $猫税| python3 -c "import sys; print(sum(int(x)for x in sys.stdin))"トレースバック(最新の呼び出しは最後):ファイル "<string>"、行1、<モジュール>ファイル "<string > "、1行目<genexpr>のValueError:基数10のint()のリテラルが無効: '\ n'
明るい


5

statがあると、ls -lとcut全体がかなり複雑になります。また、ls -lの正確な形式に対して脆弱です(cutの列番号を変更するまで機能しませんでした)

また、無駄な使い方を修正しました。

<files.txt  xargs stat -c %s | paste -sd+ - | bc

2
ええと。32年間Unixを使用していて、それ<infile commandが同じである(そしてより良い順序で)ことを決して知りませんでしたcommand <infile
Camille Goudeseune 16

5

bcがインストールされていない場合は、

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

の代わりに

... | paste -sd+ - | bc

$( ) <-コマンド実行の値を返します

$(( 1+2 )) <-評価された結果を返す

echo <-画面にエコーする


4

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

3

代わりに「du」を使用します。

$ cat files.txt | xargs du -c | tail -1
4480    total

数値だけが必要な場合:

cat files.txt | xargs du -c | tail -1 | awk '{print $1}'

5
ディスク使用量!=ファイルのサイズ。duはディスク使用量を報告します。
0x6adb015 2009年

4
-bスイッチを指定すると、duで必要なことができるようになります。
RichieHindle、2009年

@ 0x6adb015良い知識。気づかなかったおかげで。
MichaelJones、2009年

3
これは、OPが数値の列を追加したいという特定の理由に対する有用な回答ですが、一般的な数値の追加の場合には不十分です。(私はいつも自分で "du"を使用していますが、コマンドラインの数学を求めてここに来ました。:
Michael H.

12
files.txtが大きい場合、これは機能しません。パイプで渡された引数の数xargsが特定のしきい値に達すると、への複数の呼び出しに分割されduます。最後に表示される合計は、duリスト全体ではなく、への最後の呼び出しの合計です。
マシューシモノー

3

kshの場合:

echo " 0 $(ls -l $(<files.txt) | awk '{print $5}' | tr '\n' '+') 0" | bc

1
をスキップしてピックアップcutするのに適していますが、数学を実行するawks機能を無視します...
dmckee --- ex-moderator kitten

1

gawkへのパイプ:

 cat files.txt | xargs ls -l | cut -c 23-30 | gawk 'BEGIN { sum = 0 } // { sum = sum + $0 } END { print sum }'

1

ここにあります

cat files.txt | xargs ls -l | cut -c 23-30 | sed -e :a -e '$!N;s/\n/+/;ta' | bc

6
+1は、perlよりも醜い言語があることをすべて証明するためのものです:)
bdonlan

1
#
#       @(#) addup.sh 1.0 90/07/19
#
#       Copyright (C) <heh> SjB, 1990
#       Adds up a column (default=last) of numbers in a file.
#       95/05/16 updated to allow (999) negative style numbers.


case $1 in

-[0-9])

        COLUMN=`echo $1 | tr -d -`

        shift

;;

*)

        COLUMN="NF"

;;

esac

echo "Adding up column .. $COLUMN .. of file(s) .. $*"

nawk  ' OFMT="%.2f"                                       # 1 "%12.2f"

        { x = '$COLUMN'                                   # 2

          neg = index($x, "$")                            # 3

          if (neg > 0) X = gsub("\\$", "", $x)

          neg = index($x, ",")                            # 4

          if (neg > 1) X = gsub(",", "", $x)

          neg = index($x, "(")                            # 8 neg (123 & change

          if (neg > 0) X = gsub("\\(", "", $x)

          if (neg > 0) $x = (-1 * $x)                     # it to "-123.00"

          neg = index($x, "-")                            # 5

          if (neg > 1) $x = (-1 * $x)                     # 6

          t += $x                                         # 7

          print "x is <<<", $x+0, ">>> running balance:", t

        } ' $*


# 1.  set numeric format to eliminate rounding errors
# 1.1 had to reset numeric format from 12.2f to .2f 95/05/16
#     when a computed number is assigned to a variable ( $x = (-1 * $x) )
#     it causes $x to use the OFMT so -1.23 = "________-1.23" vs "-1.23"
#     and that causes my #5 (negative check) to not work correctly because
#     the index returns a number >1 and to the neg neg than becomes a positive
#     this only occurs if the number happened to b a "(" neg number
# 2.  find the field we want to add up (comes from the shell or defaults
#     to the last field "NF") in the file
# 3.  check for a dollar sign ($) in the number - if there get rid of it
#     so we may add it correctly - $12 $1$2 $1$2$ $$1$$2$$ all = 12
# 4.  check for a comma (,) in the number - if there get rid of it so we
#     may add it correctly - 1,2 12, 1,,2 1,,2,, all = 12   (,12=0)
# 5.  check for negative numbers
# 6.  if x is a negative number in the form 999- "make" it a recognized
#     number like -999 - if x is a negative number like -999 already
#     the test fails (y is not >1) and this "true" negative is not made
#     positive
# 7.  accumulate the total
# 8.  if x is a negative number in the form (999) "make it a recognized
#     number like -999
# * Note that a (-9) (neg neg number) returns a postive
# * Mite not work rite with all forms of all numbers using $-,+. etc. *

1

使いたい…。

echo "
1
2
3 " | sed -e 's,$, + p,g' | dc 

各行の合計が表示されます...

この状況に適用:

ls -ld $(< file.txt) | awk '{print $5}' | sed -e 's,$, + p,g' | dc 

合計は最後の値です...


1
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

パイプも必要ありません:awk '{ total += $1} END {print total}' files.txtより高速です
bmv

0

私の意見では、これに対する最も簡単な解決策は "expr" unixコマンドです:

s=0; 
for i in `cat files.txt | xargs ls -l | cut -c 23-30`
do
   s=`expr $s + $i`
done
echo $s


0
sizes=( $(cat files.txt | xargs ls -l | cut -c 23-30) )
total=$(( $(IFS="+"; echo "${sizes[*]}") ))

または、サイズを読んでそれらを合計することもできます

declare -i total=0
while read x; total+=x; done < <( cat files.txt | xargs ls -l | cut -c 23-30 )

一口サイズとブロックを気にしない場合は、次に

declare -i total=0
while read s junk; total+=s; done < <( cat files.txt | xargs ls -s )

0

Rを使用している場合は、以下を使用できます。

> ... | Rscript -e 'print(sum(scan("stdin")));'
Read 4 items
[1] 2232320

私はRに慣れているので、実際にはこのようなものにいくつかのエイリアスがあるので、bashこの構文を覚えていなくても使用できます。例えば:

alias Rsum=$'Rscript -e \'print(sum(scan("stdin")));\''

どっちにしよう

> ... | Rsum
Read 4 items
[1] 2232320

インスピレーション:単一のコマンドで数値のリストの最小、最大、中央値、平均を取得する方法はありますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.