Ajoutez une colonne de chiffres au shell Unix

198

Étant donné une liste de fichiers files.txt, je peux obtenir une liste de leurs tailles comme ceci:

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

qui produit quelque chose comme ça:

  151552
  319488
 1536000
  225280

Comment puis-je obtenir le total de tous ces chiffres?

RichieHindle
la source

Réponses:

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

est le plus court que j'ai trouvé (sur le blog UNIX Command Line ).

Edit: ajout de l' -argument de la portabilité, merci @Dogbert et @Owen.

Todd Owen
la source
Agréable. Besoin du dernier - sur Solaris aussi
Owen B
8
alias sum="paste -sd+ - | bc"ajouté à l'achèvement du shell, merci mate
slf
. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))si vous voulez tout bash tout le temps:
qneill
13
@slf, attention, vous venez de surcharger/usr/bin/sum
qneill
3
Attention, bcn'est pas disponible sur certains systèmes! awk, d'autre part est (je crois) requis pour la conformité POSIX.
vktec
154

Voici

cat files.txt | xargs ls -l | cut -c 23-30 | 
  awk '{total = total + $1}END{print total}'
Greg Reynolds
la source
34
Utiliser awk est une bonne idée, mais pourquoi garder le cut? C'est un numéro de colonne prévisible, alors utilisez... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- chaton ex-modérateur
3
Vous avez raison bien sûr - il était plus facile de simplement ajouter à la fin de ce qui était déjà là :-)
Greg Reynolds
2
Un crochet de trop dans la réponse de @ dmckee :)
Dr. Jan-Philip Gehrcke
7
Pour rendre cela un peu plus court, vous pouvez utiliser à la total+=$1place detotal = total + $1
vktec
10

Au lieu d'utiliser cut pour obtenir la taille du fichier à partir de la sortie de ls -l , vous pouvez utiliser directement:

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

Awk interprète "$ 5" comme cinquième colonne. Il s'agit de la colonne de ls -l qui vous donne la taille du fichier.

Barun
la source
10

cat ne fonctionnera pas s'il y a des espaces dans les noms de fichiers. voici un perl one-liner à la place.

perl -nle 'chomp; $x+=(stat($_))[7]; END{print $x}' files.txt
TOUT
la source
8
python3 -c"import os; print(sum(os.path.getsize(f) for f in open('files.txt').read().split()))"

Ou si vous voulez simplement additionner les chiffres, dirigez-vous vers:

python3 -c"import sys; print(sum(int(x) for x in sys.stdin))"
Collin Anderson
la source
1
... | python -c'import sys; print(sum(int(x) for x in sys.stdin))'lorsque python 2 disparaît à la fin de cette année.
Éponyme
don @ oysters: ~ / Documents $ cat tax | python3 -c "import sys; print (sum (int (x) for x in sys.stdin))" Traceback (dernier appel en date): Fichier "<string>", ligne 1, dans <module> File "<string > ", ligne 1, dans <genexpr> ValueError: littéral non valide pour int () avec la base 10: '\ n'
don bright le
5

TMTWWTDI : Perl a un opérateur de taille de fichier (-s)

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

L'ensemble ls -l puis cut est plutôt alambiqué quand on a stat . Il est également vulnérable au format exact de ls -l (cela n'a pas fonctionné jusqu'à ce que j'ai changé les numéros de colonne pour couper )

En outre, fixe l' utilisation inutile de chat .

<files.txt  xargs stat -c %s | paste -sd+ - | bc
Hugo González Monteverde
la source
2
Huh. J'utilise Unix depuis 32 ans et je n'ai jamais su que c'était <infile commandla même chose (et dans un meilleur ordre que) command <infile.
Camille Goudeseune
5

si vous n'avez pas installé bc, essayez

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

au lieu de

... | paste -sd+ - | bc

$( ) <- retourne la valeur de l'exécution de la commande

$(( 1+2 )) <- retourne les résultats évalués

echo <- l'écho à l'écran

MrMobileMan
la source
4

Vous pouvez utiliser le script suivant si vous souhaitez simplement utiliser des scripts shell sans awk ou d'autres interprètes:

#!/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
la source
3

J'utiliserais plutôt "du".

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

Si vous voulez juste le numéro:

cat files.txt | xargs du -c | tail -1 | awk '{print $1}'
MichaelJones
la source
5
Utilisation du disque! = Taille du fichier. du signale l'utilisation du disque.
0x6adb015
4
Je pense que le commutateur -b fait du faire ce dont j'ai besoin.
RichieHindle
@ 0x6adb015 Bonne connaissance. Merci je ne l'avais pas réalisé.
MichaelJones
3
C'est une réponse utile pour la raison précise pour laquelle le PO voulait que la colonne de chiffres soit ajoutée, mais pour le cas général de la numérotation, elle est insuffisante. (J'utilise "du" tout le temps moi-même, mais je suis venu ici à la recherche de mathématiques en ligne de commande. :-))
Michael H.
12
Cela ne fonctionnera pas quand files.txtest grand. Si le nombre d'arguments canalisés pour xargsatteindre un certain seuil, il les décompose sur plusieurs appels à du. Le total affiché à la fin est le total pour le dernier appel à du, pas la liste entière.
Matthew Simoneau
3

Dans ksh:

echo " 0 $(ls -l $(<files.txt) | awk '{print $5}' | tr '\n' '+') 0" | bc
Sanjaya R
la source
1
Bon pour reprendre le saut cut, mais vous ignorez la capacité des awks à faire le calcul ...
dmckee --- chaton ex-modérateur
1

Pipe à gawk:

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

Voici la mienne

cat files.txt | xargs ls -l | cut -c 23-30 | sed -e :a -e '$!N;s/\n/+/;ta' | bc
Jason Punyon
la source
6
+1 pour avoir prouvé une fois pour toutes qu'il existe des langues plus
laides
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
la source
1

J'aime utiliser ....

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

ils montreront la somme de chaque ligne ...

appliquer sur cette situation:

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

Le total est la dernière valeur ...

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

Vous pouvez utiliser le awk pour faire de même, il ignore même les non entiers

$ cat files.txt
1
2.3
3.4
ew
1

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

ou vous pouvez utiliser la commande ls et calculer la sortie lisible par l'homme

$ 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
la source
Vous n'avez même pas besoin de pipe: awk '{ total += $1} END {print total}' files.txtc'est plus rapide
bmv
0

À mon avis, la solution la plus simple est la commande 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
la source
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
la source
0
sizes=( $(cat files.txt | xargs ls -l | cut -c 23-30) )
total=$(( $(IFS="+"; echo "${sizes[*]}") ))

Ou vous pouvez simplement les additionner en lisant les tailles

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

Si vous ne vous souciez pas de la taille des bouchées et des blocs, c'est bien, alors

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

Si vous avez R, vous pouvez utiliser:

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

Comme je suis à l'aise avec R, j'ai en fait plusieurs alias pour des choses comme ça, donc je peux les utiliser bashsans avoir à me souvenir de cette syntaxe. Par exemple:

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

qui me laisse faire

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

Inspiration: Existe - t-il un moyen d'obtenir le minimum, le maximum, la médiane et la moyenne d'une liste de nombres en une seule commande?

merv
la source