Suma una columna de números en el shell de Unix


198

Dada una lista de archivos files.txt, puedo obtener una lista de sus tamaños de esta manera:

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

que produce algo como esto:

  151552
  319488
 1536000
  225280

¿Cómo puedo obtener el total de todos esos números?

Respuestas:


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

es el más corto que he encontrado (del blog UNIX Command Line ).

Editar: se agregó el -argumento para la portabilidad, gracias @Dogbert y @Owen.


Agradable. Necesito lo último - también en Solaris
Owen B

8
alias sum="paste -sd+ - | bc"agregado a la finalización del shell, gracias amigo
slf

. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))si quieres toda la fiesta todo el tiempo:
qneill

13
@slf, cuidado, acabas de sobrecargar/usr/bin/sum
qneill

3
¡Cuidado, bcno está disponible en algunos sistemas! awk, por otro lado, es (creo) necesario para el cumplimiento de POSIX.
vktec

154

Aquí va

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

34
Usar awk es una buena idea, pero ¿por qué quedarse con el cut? Eso es un número de columna predecible, por lo que su uso... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- ex-moderador gatito

3
Tienes razón, por supuesto, fue más fácil simplemente agregar al final de lo que ya estaba allí :-)
Greg Reynolds

2
Un paréntesis demasiado en la respuesta de @ dmckee :)
Dr. Jan-Philip Gehrcke

77
Para hacer esto un poco más corto, puede usar en total+=$1lugar detotal = total + $1
vktec

10

En lugar de usar cut para obtener el tamaño del archivo de la salida de ls -l , puede usar directamente:

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

Awk interpreta "$ 5" como la quinta columna. Esta es la columna de ls -l que le da el tamaño del archivo.


10

cat no funcionará si hay espacios en los nombres de archivo. aquí hay un perl one-liner en su lugar.

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 si solo desea sumar los números, ingrese:

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))'cuando Python 2 desaparece a finales de este año.
Epónimo

don @ ostras: ~ / Documentos $ impuesto sobre gatos | python3 -c "import sys; print (sum (int (x) for x in sys.stdin))" Traceback (última llamada más reciente): Archivo "<cadena>", línea 1, en <módulo> Archivo "<cadena > ", línea 1, en <genexpr> ValueError: literal no válido para int () con base 10: '\ n'
don brillante

5

TMTWWTDI : Perl tiene un operador de tamaño de archivo (-s)

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

5

Todo ls -l y luego cut es bastante complicado cuando tienes stat . También es vulnerable al formato exacto de ls -l (no funcionó hasta que cambié los números de columna para cortar )

Además, reparó el uso inútil del gato .

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

2
Huh He estado usando Unix durante 32 años, y nunca supe que <infile commandes lo mismo que (y en un orden mejor que) command <infile.
Camille Goudeseune

5

si no tiene instalado bc, intente

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

en vez de

... | paste -sd+ - | bc

$( ) <- devuelve el valor de ejecutar el comando

$(( 1+2 )) <- devuelve los resultados evaluados

echo <- repítelo en la pantalla


4

Puede usar el siguiente script si solo desea usar scripts de shell sin awk u otros intérpretes:

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

Yo usaría "du" en su lugar.

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

Si solo quieres el número:

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

55
Uso del disco! = Tamaño del archivo. du informa el uso del disco.
0x6adb015

44
Creo que el modificador -b hace que du haga lo que necesito.
RichieHindle

@ 0x6adb015 Buen conocimiento. Gracias no me había dado cuenta.
MichaelJones

3
Esa es una respuesta útil por la razón específica por la cual el OP quería que se agregara la columna de números, pero para el caso general de sumar números, se queda corto. (Yo uso "du" todo el tiempo, pero vine aquí buscando matemática de línea de comandos. :-))
Michael H.

12
Esto no funcionará cuando files.txtsea ​​grande. Si el número de argumentos canalizados xargsalcanza cierto umbral, los divide en varias llamadas a du. El total que se muestra al final es el total de la última llamada du, no la lista completa.
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

Aquí está el mío

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

66
+1 por probar de una vez por todas que hay idiomas más feos que 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

Me gusta usar ....

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

mostrarán la suma de cada línea ...

aplicando sobre esta situación:

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

Total es el último valor ...


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

Puede usar el awk para hacer lo mismo, incluso omite los no enteros

$ cat files.txt
1
2.3
3.4
ew
1

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

o puede usar el comando ls y calcular la salida legible por humanos

$ 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

Ni siquiera necesita tubería: awk '{ total += $1} END {print total}' files.txtes más rápido
bmv

0

En mi opinión, la solución más simple para esto es el comando "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

Puro golpe

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

O podrías simplemente sumarlos mientras lees los tamaños

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

Si no te importan los tamaños de mordida y los bloques está bien, entonces solo

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

0

Si tiene R, puede usar:

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

Como me siento cómodo con R, en realidad tengo varios alias para cosas como esta, así que puedo usarlos bashsin tener que recordar esta sintaxis. Por ejemplo:

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

que me deja hacer

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

Inspiración: ¿Hay alguna manera de obtener el mínimo, el máximo, la mediana y el promedio de una lista de números en un solo comando?

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.