Tambahkan kolom angka di shell Unix

198

Diberikan daftar file dalam files.txt , saya bisa mendapatkan daftar ukurannya seperti ini:

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

yang menghasilkan sesuatu seperti ini:

  151552
  319488
 1536000
  225280

Bagaimana saya bisa mendapatkan total semua angka itu?

RichieHindle
sumber

Jawaban:

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

adalah yang terpendek yang saya temukan (dari blog UNIX Command Line ).

Sunting: menambahkan -argumen untuk portabilitas, terima kasih @Dogbert dan @Owen.

Todd Owen
sumber
Bagus. Butuh yang terakhir - di Solaris juga
Owen B
8
alias sum="paste -sd+ - | bc"ditambahkan ke penyelesaian shell, terima kasih sobat
slf
. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))jika Anda ingin semua bash sepanjang waktu:
qneill
13
@ SFF, hati-hati, Anda baru saja kelebihan muatan/usr/bin/sum
qneill
3
Hati-hati, bctidak tersedia di beberapa sistem! awk, di sisi lain adalah (saya percaya) diperlukan untuk kepatuhan POSIX.
vktec
154

Ini dia

cat files.txt | xargs ls -l | cut -c 23-30 | 
  awk '{total = total + $1}END{print total}'
Greg Reynolds
sumber
34
Menggunakan awk adalah ide yang bagus, tetapi mengapa tetap melakukannya cut? Itu nomor kolom yang dapat diprediksi, jadi gunakan... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- ex-moderator kitten
3
Anda benar tentu saja - lebih mudah hanya menambahkan ke akhir apa yang sudah ada :-)
Greg Reynolds
2
Satu braket terlalu banyak dalam jawaban @ dmckee :)
Dr. Jan-Philip Gehrcke
7
Untuk membuat ini sedikit lebih pendek, Anda bisa menggunakan total+=$1bukannyatotal = total + $1
vktec
10

Alih-alih menggunakan cut untuk mendapatkan ukuran file dari output ls-l , Anda dapat menggunakan langsung:

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

Awk mengartikan "$ 5" sebagai kolom kelima. Ini adalah kolom dari ls-l yang memberi Anda ukuran file.

Barun
sumber
10

cat tidak akan berfungsi jika ada spasi di nama file. di sini adalah perl one-liner sebagai gantinya.

perl -nle 'chomp; $x+=(stat($_))[7]; END{print $x}' files.txt
SEMUA
sumber
8
python3 -c"import os; print(sum(os.path.getsize(f) for f in open('files.txt').read().split()))"

Atau jika Anda hanya ingin menjumlahkan angka, masukkan:

python3 -c"import sys; print(sum(int(x) for x in sys.stdin))"
Collin Anderson
sumber
1
... | python -c'import sys; print(sum(int(x) for x in sys.stdin))'ketika python 2 menghilang pada akhir tahun ini.
Eponim
don @ tiram: ~ / Dokumen $ cat tax | python3 -c "import sys; print (jumlah (int (x) untuk x di sys.stdin))" Traceback (panggilan terakhir terakhir): File "<string>", baris 1, dalam <module> File "<string > ", baris 1, dalam <genexpr> ValueError: literal tidak valid untuk int () dengan basis 10: '\ n'
don bright
5

TMTWWTDI : Perl memiliki operator ukuran file (-s)

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

Seluruh ls -l dan kemudian dipotong agak berbelit-belit ketika Anda memiliki stat . Itu juga rentan terhadap format persis ls-l (tidak berfungsi sampai saya mengubah nomor kolom untuk dipotong )

Juga, perbaiki penggunaan kucing yang tidak berguna .

<files.txt  xargs stat -c %s | paste -sd+ - | bc
Hugo González Monteverde
sumber
2
Hah. Telah menggunakan Unix selama 32 tahun, dan tidak pernah tahu bahwa <infile commanditu sama dengan (dan dalam urutan yang lebih baik daripada) command <infile.
Camille Goudeseune
5

jika Anda belum menginstal bc, coba

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

dari pada

... | paste -sd+ - | bc

$( ) <- mengembalikan nilai mengeksekusi perintah

$(( 1+2 )) <- kembalikan hasil yang dievaluasi

echo <- gema ke layar

MrMobileMan
sumber
4

Anda dapat menggunakan skrip berikut jika Anda hanya ingin menggunakan skrip shell tanpa awk atau penerjemah lain:

#!/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
Andre Miller
sumber
3

Saya akan menggunakan "du" sebagai gantinya.

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

Jika Anda hanya menginginkan nomornya:

cat files.txt | xargs du -c | tail -1 | awk '{print $1}'
MichaelJones
sumber
5
Penggunaan disk! = Ukuran file. du melaporkan penggunaan disk.
0x6adb015
4
Saya pikir -b switch membuat du melakukan apa yang saya butuhkan.
RichieHindle
@ 0x6adb015 Pengetahuan yang bagus. Terima kasih saya tidak menyadari.
MichaelJones
3
Itu jawaban yang berguna untuk alasan spesifik mengapa OP menginginkan kolom angka ditambahkan, tetapi untuk kasus penambahan angka umum, itu gagal. (Saya sendiri menggunakan "du" sepanjang waktu, tetapi saya datang ke sini mencari matematika baris perintah. :-))
Michael H.
12
Ini tidak akan berfungsi ketika files.txtbesar. Jika jumlah argumen disalurkan untuk xargsmencapai ambang tertentu, itu terpecah atas beberapa panggilan ke du. Total yang ditunjukkan di akhir adalah total untuk panggilan terakhir du, bukan seluruh daftar.
Matthew Simoneau
3

Dalam ksh:

echo " 0 $(ls -l $(<files.txt) | awk '{print $5}' | tr '\n' '+') 0" | bc
Sanjaya R
sumber
1
Baik untuk mengambil di melewatkan cut, tetapi Anda mengabaikan awks kemampuan untuk melakukan matematika ...
dmckee --- mantan moderator kucing
1

Pipa untuk melongo:

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

Ini milik saya

cat files.txt | xargs ls -l | cut -c 23-30 | sed -e :a -e '$!N;s/\n/+/;ta' | bc
Jason Punyon
sumber
6
1 untuk membuktikan sekali dan untuk semua bahwa ada bahasa jelek daripada 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. *
steven bensky
sumber
1

Saya suka menggunakan ....

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

mereka akan menunjukkan jumlah setiap baris ...

menerapkan situasi ini:

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

Total adalah nilai terakhir ...

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

Anda dapat menggunakan awk untuk melakukan hal yang sama bahkan melompati non integer

$ cat files.txt
1
2.3
3.4
ew
1

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

atau Anda dapat menggunakan perintah ls dan menghitung output yang dapat dibaca manusia

$ 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
ck reddy
sumber
Anda bahkan tidak memerlukan pipa: awk '{ total += $1} END {print total}' files.txtlebih cepat
bmv
0

Menurut pendapat saya, solusi paling sederhana untuk ini adalah perintah "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
zsram
sumber
0

Bash murni

total=0; for i in $(cat files.txt | xargs ls -l | cut -c 23-30); do 
total=$(( $total + $i )); done; echo $total
John Kloian
sumber
0
sizes=( $(cat files.txt | xargs ls -l | cut -c 23-30) )
total=$(( $(IFS="+"; echo "${sizes[*]}") ))

Atau Anda bisa menjumlahkannya saat Anda membaca ukurannya

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

Jika Anda tidak peduli tentang ukuran dan blok gigitan tidak masalah, maka adil

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

Jika Anda memiliki R, Anda dapat menggunakan:

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

Karena saya nyaman dengan R, saya sebenarnya memiliki beberapa alias untuk hal-hal seperti ini sehingga saya dapat menggunakannya bashtanpa harus mengingat sintaks ini. Misalnya:

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

yang mari saya lakukan

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

Inspirasi: Apakah ada cara untuk mendapatkan min, max, median, dan rata-rata daftar angka dalam satu perintah?

merv
sumber