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?

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.


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}'

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.


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

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))"

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

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

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


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

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}'

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


1

Pipa untuk melongo:

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

1

Ini milik saya

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

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. *

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 ...


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

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

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

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 )

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?

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.