Aggiungi una colonna di numeri nella shell Unix


198

Dato un elenco di file in files.txt, posso ottenere un elenco delle loro dimensioni in questo modo:

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

che produce qualcosa del genere:

  151552
  319488
 1536000
  225280

Come posso ottenere il totale di tutti quei numeri?

Risposte:


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

è il più corto che ho trovato (dal blog della riga di comando di UNIX ).

Modifica: aggiunto l' -argomento per la portabilità, grazie a @Dogbert e @Owen.


Bello. Ho bisogno dell'ultimo - anche su Solaris
Owen B,

8
alias sum="paste -sd+ - | bc"aggiunto al completamento della shell, grazie mate
slf,

. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))se vuoi tutto bash sempre:
qneill

13
@lf, attenzione, hai appena sovraccaricato/usr/bin/sum
qneill

3
Attenzione, bcnon è disponibile su alcuni sistemi! awk, d'altra parte è (credo) necessario per la conformità POSIX.
vktec,

154

Ecco qui

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

34
Usare awk è una buona idea, ma perché mantenere il cut? È un numero di colonna prevedibile, quindi usa... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- ex gattino moderatore

3
Hai ragione ovviamente - è stato più semplice aggiungere alla fine ciò che era già lì :-)
Greg Reynolds,

2
Una parentesi troppo nella risposta di @ dmckee :)
Dr. Jan-Philip Gehrcke,

7
Per renderlo un po 'più breve, puoi usare total+=$1invece ditotal = total + $1
vktec il

10

Invece di usare cut per ottenere la dimensione del file dall'output di ls -l , puoi usare direttamente:

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

Awk interpreta "$ 5" come la quinta colonna. Questa è la colonna da ls -l che ti dà le dimensioni del file.


10

cat non funzionerà se ci sono spazi nei nomi dei file. qui invece è un perl one-liner.

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

O se vuoi semplicemente sommare i numeri, inserisci:

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))'quando python 2 scompare alla fine di quest'anno.
Eponimo

don @ oysters: ~ / Documents $ cat tax | python3 -c "import sys; print (sum (int (x) for x in sys.stdin))" Traceback (ultima chiamata più recente): File "<string>", riga 1, in <module> File "<string > ", riga 1, in <genexpr> ValueError: valore letterale non valido per int () con base 10: '\ n'
don bright

5

TMTWWTDI : Perl ha un operatore dimensioni file (-s)

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

5

L'intero ls -l e quindi il taglio sono piuttosto contorti quando si ha stat . È anche vulnerabile al formato esatto di ls -l (non ha funzionato fino a quando non ho cambiato i numeri di colonna per il taglio )

Inoltre, risolto l' uso inutile di cat .

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

2
Huh. Uso Unix da 32 anni e non ho mai saputo che <infile commandfosse lo stesso di (e in un ordine migliore di) command <infile.
Camille Goudeseune,

5

se non hai installato bc, prova

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

invece di

... | paste -sd+ - | bc

$( ) <- restituisce il valore dell'esecuzione del comando

$(( 1+2 )) <- restituisce i risultati valutati

echo <- echo sullo schermo


4

È possibile utilizzare il seguente script se si desidera utilizzare solo script di shell senza awk o altri interpreti:

#!/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

Vorrei invece usare "du".

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

Se vuoi solo il numero:

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

5
Utilizzo del disco! = Dimensione del file. du riporta l'utilizzo del disco.
0x6adb015,

4
Penso che l'opzione -b faccia fare a du quello che mi serve.
RichieHindle,

@ 0x6adb015 Buona conoscenza. Grazie non me ne ero reso conto.
Michael Jones,

3
Questa è una risposta utile per il motivo specifico per cui l'OP ha voluto aggiungere la colonna di numeri, ma per il caso generale di aggiunta di numeri, non è all'altezza. (Uso sempre "du" da solo, ma sono venuto qui in cerca di matematica da riga di comando. :-))
Michael H.

12
Questo non funzionerà quando files.txtè grande. Se il numero di argomenti reindirizzati xargsraggiunge una determinata soglia, li suddivide su più chiamate a du. Il totale mostrato alla fine è il totale dell'ultima chiamata du, non dell'intero elenco.
Matthew Simoneau,


1

Pipe to gawk:

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

1

Ecco il mio

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

6
+1 per dimostrare una volta per tutte che ci sono lingue più
brutte

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

Mi piace usare ....

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

mostreranno la somma di ogni riga ...

applicando su questa situazione:

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

Il totale è l'ultimo valore ...


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

Puoi usare awk per fare lo stesso anche saltando i non interi

$ cat files.txt
1
2.3
3.4
ew
1

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

oppure è possibile utilizzare il comando ls e calcolare l'output leggibile dall'uomo

$ 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

Non hai nemmeno bisogno di pipe: awk '{ total += $1} END {print total}' files.txtè più veloce
bmv

0

Secondo me, la soluzione più semplice a questo è il comando 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

Bash puro

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[*]}") ))

Oppure potresti semplicemente sommarli mentre leggi le dimensioni

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

Se non ti importa delle dimensioni del morso e dei blocchi va bene, allora basta

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

0

Se hai R, puoi usare:

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

Dato che mi sento a mio agio con R, in realtà ho diversi alias per cose come questa, quindi posso usarle bashsenza dover ricordare questa sintassi. Per esempio:

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

che mi faccia fare

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

Ispirazione: c'è un modo per ottenere il minimo, il massimo, la mediana e la media di un elenco di numeri in un singolo comando?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.