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?

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.


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

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.


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

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

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

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

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


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

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

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,


1

Rura do gawk:

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

1

To moje

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

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

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


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

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

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

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 )

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?

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.