Dodaj kolumnę liczb w powłoce uniksowej

198

Biorąc pod uwagę listę plików files.txt, mogę uzyskać listę takich rozmiarów:

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

co daje coś takiego:

  151552
  319488
 1536000
  225280

Jak mogę uzyskać sumę wszystkich tych liczb?

RichieHindle
źródło

Odpowiedzi:

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

jest najkrótszy, jaki znalazłem (z bloga wiersza poleceń systemu UNIX ).

Edycja: dodano -argument za przenośnością, dzięki @Dogbert i @Owen.

Todd Owen
źródło
Miły. Potrzebuję ostatniego - również w Solarisie
Owena B
8
alias sum="paste -sd+ - | bc"dodano do ukończenia powłoki, dzięki kolego
slf
. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))jeśli chcesz cały bash cały czas:
qneill
13
@slf, uważaj, właśnie przeładowałeś/usr/bin/sum
qneill
3
Uwaga, bcnie jest dostępny w niektórych systemach! awk, z drugiej strony jest (jak sądzę) wymagany do zgodności z POSIX.
vktec
154

Tutaj idzie

cat files.txt | xargs ls -l | cut -c 23-30 | 
  awk '{total = total + $1}END{print total}'
Greg Reynolds
źródło
34
Korzystanie z awk jest dobrym pomysłem, ale po co to robić cut? To przewidywalny numer kolumny, więc użyj... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- były moderator kociak
3
Oczywiście masz rację - łatwiej było po prostu dołączyć na końcu tego, co już było :-)
Greg Reynolds
2
Jeden nawias za dużo w odpowiedzi @ dmckee :)
Dr Jan-Philip Gehrcke
7
Aby było to nieco krótsze, możesz użyć total+=$1zamiasttotal = total + $1
vktec
10

Zamiast używać cut do uzyskania rozmiaru pliku z wyjścia ls -l , możesz użyć bezpośrednio:

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

Awk interpretuje „5 $” jako piątą kolumnę. To jest kolumna od ls -l, która podaje rozmiar pliku.

Barun
źródło
10

cat nie będzie działać, jeśli w nazwach plików są spacje. tutaj zamiast tego jest jeden liniowy perl.

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

Lub jeśli chcesz tylko zsumować liczby, potokuj w:

python3 -c"import sys; print(sum(int(x) for x in sys.stdin))"
Collin Anderson
źródło
1
... | python -c'import sys; print(sum(int(x) for x in sys.stdin))'kiedy Python 2 znika pod koniec tego roku.
Tytułowy
don @ ostrygi: ~ / Documents $ cat tax | python3 -c "import sys; print (sum (int (x) for x in sys.stdin))" Traceback (ostatnie ostatnie wywołanie): Plik "<ciąg>", wiersz 1, w <module> Plik "<ciąg > ", wiersz 1, w <genexpr> ValueError: niepoprawny literał dla int () z bazą 10: '\ n'
don bright
5

TMTWWTDI : Perl ma operator wielkości pliku (-s)

perl -lne '$t+=-s;END{print $t}' files.txt
Kitzkikz
źródło
5

Całe ls -l, a następnie cięcie jest raczej skomplikowane, gdy masz stat . Jest także podatny na dokładny format ls -l (nie działał, dopóki nie zmieniłem numerów kolumn dla wycinania )

Naprawiono także bezużyteczne korzystanie z cat .

<files.txt  xargs stat -c %s | paste -sd+ - | bc
Hugo González Monteverde
źródło
2
Huh Używam Uniksa od 32 lat i nigdy nie wiedziałem, że <infile commandto samo (w lepszej kolejności niż) command <infile.
Camille Goudeseune,
5

jeśli nie masz zainstalowanego bc, spróbuj

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

zamiast

... | paste -sd+ - | bc

$( ) <- zwraca wartość wykonania polecenia

$(( 1+2 )) <- zwraca ocenione wyniki

echo <- wyświetl echo na ekranie

MrMobileMan
źródło
4

Możesz użyć następującego skryptu, jeśli chcesz używać skryptów powłoki bez awk lub innych interpreterów:

#!/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
źródło
3

Zamiast tego użyłbym „du”.

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

Jeśli chcesz tylko numer:

cat files.txt | xargs du -c | tail -1 | awk '{print $1}'
MichaelJones
źródło
5
Wykorzystanie dysku! = Rozmiar pliku. du zgłasza użycie dysku.
0x6adb015
4
Myślę, że przełącznik -b sprawia, że ​​du robi to, czego potrzebuję.
RichieHindle
@ 0x6adb015 Dobra wiedza. Dzięki, nie zdawałem sobie sprawy.
MichaelJones
3
To przydatna odpowiedź z konkretnego powodu, dla którego OP chciał dodać kolumnę liczb, ale w przypadku ogólnego dodawania liczb jest ona niewystarczająca. (Cały czas sam używam „du”, ale przyszedłem tutaj, szukając matematyki z wiersza poleceń. :-))
Michael H.
12
To nie zadziała, gdy files.txtjest duży. Jeśli liczba przekazanych argumentów do xargsokreślonego progu osiąga określony próg, dzieli je na wiele wywołań do du. Suma pokazana na końcu jest sumą tylko ostatniego połączenia du, a nie całej listy.
Matthew Simoneau,
3

W ksh:

echo " 0 $(ls -l $(<files.txt) | awk '{print $5}' | tr '\n' '+') 0" | bc
Sanjaya R.
źródło
1
Dobry do wzięcia udziału w pomijaniu cut, ale ignorujesz umiejętność matematyki do wykonywania matematyki ...
dmckee --- były moderator kotek
1

Rura do gawk:

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

To moje

cat files.txt | xargs ls -l | cut -c 23-30 | sed -e :a -e '$!N;s/\n/+/;ta' | bc
Jason Punyon
źródło
6
+1 za udowodnienie raz na zawsze, że istnieją brzydsze języki niż 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
źródło
1

Lubię używać ....

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

pokażą sumę każdej linii ...

zastosowanie w tej sytuacji:

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

Suma jest ostatnią wartością ...

ceinmart
źródło
1
cat files.txt | awk '{ total += $1} END {print total}'

Możesz użyć awk, aby zrobić to samo, nawet pomija liczby całkowite

$ cat files.txt
1
2.3
3.4
ew
1

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

lub możesz użyć polecenia ls i obliczyć dane wyjściowe czytelne dla człowieka

$ 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
źródło
Nie potrzebujesz nawet fajki: awk '{ total += $1} END {print total}' files.txtjest szybszy
bmv
0

Moim zdaniem najprostszym rozwiązaniem tego jest komenda unix „expr”:

s=0; 
for i in `cat files.txt | xargs ls -l | cut -c 23-30`
do
   s=`expr $s + $i`
done
echo $s
zsram
źródło
0

Pure Bash

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

Lub możesz je po prostu zsumować, czytając rozmiary

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

Jeśli nie zależy ci na rozmiarach zgryzu i blokach jest OK, to po prostu

declare -i total=0
while read s junk; total+=s; done < <( cat files.txt | xargs ls -s )
Mario
źródło
0

Jeśli masz R, możesz użyć:

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

Ponieważ czuję się dobrze z R, mam kilka aliasów dla takich rzeczy, więc mogę ich używać bashbez konieczności pamiętania tej składni. Na przykład:

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

co ja zrobię

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

Inspiracja: Czy istnieje sposób uzyskania minimalnej, maksymalnej, mediany i średniej listy liczb w jednym poleceniu?

merv
źródło