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?

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.


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

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.


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

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

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

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

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


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

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

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


1

Pipe à gawk:

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

1

Voici la mienne

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

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

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


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

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

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

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 )

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?

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.