2009-05-29 9 views
154

Dato un elenco di file in files.txt, posso ottenere una lista delle loro dimensioni come questo:Aggiungere una colonna di numeri alla shell Unix

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

che produce qualcosa di simile a questo:

151552 
    319488 
1536000 
    225280 

Come posso ottenere il totale di tutti quei numeri?

+0

Si dovrebbe pensare di cambiare la risposta accettata a Todd Owen. La maggior parte delle persone sembra considerarlo una soluzione migliore. – Zaz

+0

@Josh: Grazie, hai ragione. – RichieHindle

risposta

309
... | paste -sd+ - | bc 

è il più breve che ho trovato (dal UNIX Command Line blog).

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

+8

Senza offesa per l'altra risposta, ma perché non è questo quello con 70 upvotes? Probabilmente perché l'altro ha 4 anni di più. – Wug

+0

Sono d'accordo, questa è chiaramente la migliore risposta. – ekerner

+0

Brillante. Davvero, tanto da imparare con quella semplice risposta. –

3

Vorrei utilizzare "du".

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

Se si desidera solo il numero:

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

Utilizzo del disco! = Dimensione del file. du segnala l'utilizzo del disco. – 0x6adb015

+4

Penso che l'opzione -b faccia fare quello che mi serve. – RichieHindle

+0

@ 0x6adb015 Buona conoscenza. Grazie non mi ero reso conto. – MichaelJones

140

qui va

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

L'uso di awk è un'ottima idea, ma perché mantenere il 'taglio'? Questo è un numero di colonna prevedibile, quindi usa '...| xargs ls -l | awk '{total = total + $ 5} {END {print total}' ' – dmckee

+2

Ovviamente hai ragione - è stato più facile accodare alla fine quello che c'era già :-) –

+1

Una parentesi troppo in @ dmckee's rispondi :) –

1

tubo a gawk:

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

È possibile utilizzare il seguente script se solo voglio usare lo scripting della shell senza awk o altro interpreti er:

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

Ecco la mia

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

+1 per dimostrare una volta per tutte che ci sono lingue più brutte di perl :) – bdonlan

3

In ksh:

echo " 0 $(ls -l $(<files.txt) | awk '{print $5}' | tr '\n' '+') 0" | bc 
+0

Buono per la raccolta saltare saltando il 'cut', ma si ignora l'abilità di awks di fare il calcolo ... – dmckee

7

Invece di usare tagliato per ottenere la dimensione del file da output del ls -l, è può usare direttamente:

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

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

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

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

perl -nle 'chomp; $x+=(stat($_))[7]; END{print $x}' files.txt 
5

TMTWWTDI: Perl ha un operatore di dimensioni del file (s)

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

Mi piace usare ....

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

mostreranno la somma di ogni riga. ..

applicazione oltre questa situazione:

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

Il totale è l'ultimo valore ...

6
cat files.txt | xargs ls -l | cut -c 23-30 | python -c'import sys; print sum(int(x) for x in sys.stdin)' 
2

L'intero ls -l e quindi tagliato è piuttosto contorto quando si dispone di stat. È anche vulnerabile al formato esatto ls -l (non ha funzionato finché ho cambiato i numeri di colonna per taglio)

Inoltre, fissato il useless use of cat.

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

Huh. Ho usato Unix per 32 anni, e non ho mai saputo che '

2

se non si dispone di bc installato, provare

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

invece di

... | paste -sd+ - | bc 

$() < - restituire il valore di esecuzione del comando

$((1+2)) < - restituisce la valutazione valutata ULT

echo < - eco allo schermo

0

A mio avviso, 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

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

Oppure potresti semplicemente sommarli mentre leggi le taglie

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

Se non si cura di dimensioni morso e blocchi è OK, poi basta

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