web-dev-qa-db-fra.com

Pourquoi ls ne combine-t-il pas avec awk les tailles exactes?

J'essaie de trouver la taille des fichiers sur mon disque dur en octets exacts, mais chaque fois que la taille devient trop grande, le nombre devient étrange (comme 1.98329e + 12). Puis-je l'empêcher de le faire ou le convertir en octets exacts?

La commande est:

ls -lR | grep -v '^d' | awk '{total += $5} END {print "Total:", total}'

Image d'octets exacts:

img

Image de nombre étrange:

img

  • Le seuil avant d'afficher les octets exacts semble se situer autour de 500 Go.
  • La commande du -sb affiche correctement les octets, quelle que soit la taille du répertoire.
  • J'ai essayé Ubuntu Gnome 15.10 64bit (japonais et anglais) et Linux Mint 17.3 Cinnamon 64bit (japonais)
  • Mes lecteurs sont ntfs alors j’ai essayé de les formater en ext4 et de les copier. Les résultats sont les mêmes que ntfs.
4
パンツ

Le problème est que MAWK (la variante AWK installée sur Ubuntu) imprime par défaut des entiers supérieurs à 2147483647 (231-1) en notation scientifique:

% awk -W version
mawk 1.3.3 Nov 1996, Copyright (C) Michael D. Brennan

compiled limits:
max NF             32767
sprintf buffer      2040
% printf '2147483647\n' | awk '{x += $1; print x}'
2147483647
% printf '2147483648\n' | awk '{x += $1; print x}'
2.14748e+09

Vous pouvez utiliser printf avec un spécificateur de format au lieu de print *:

printf '2147483648\n' | awk '{x += $1; printf "%.0f\n", x}'
% printf '2147483648\n' | awk '{x += $1; printf "%.0f\n", x}'
2147483648

Dans ton cas:

ls -lR | grep -v '^d' | awk '{total += $5} END {printf "Total:%.0f\n", total}'
ls -lR |
    grep -v '^d' |
    awk '
        {
            total += $5
        }
        END {
            printf "Total:%.0f\n", total
        }
    '

Cela forcera AWK à imprimer total en notation décimale au lieu de notation scientifique.

Cependant, sur une autre note, vous ne devriez jamais analyser ls .

Une méthode plus sensible consiste à utiliser find + stat:

find . -type f -exec stat -c '%s' {} + | awk '{total += $1} END {printf "Total:%.0f\n", total}'
find . -type f -exec stat -c '%s' {} + |
    awk '
        {
            total += $1
        }
        END {
            printf "Total:%.0f\n", total
        }
    '

* %.0f est une astuce pour créer printf des nombres plus grands que 2147483647 (231-1), qui, lorsque vous utilisez %d en tant que spécificateur de format, sera toujours imprimé en tant que 2147483647. La limite de %.0f est que l’on commence à perdre de la précision après 9007199254740992 (253), si cela vous pose un problème (merci à Rotsor pour les informations utiles).

5
kos

TL; DR: ls et awk ne sont pas nécessaires pour vous. Utilisez du -cb ou du -bs sur le répertoire que vous souhaitez analyser.

Votre but est de

  1. Trouver tous les fichiers
  2. trouver leur taille (en octets)
  3. produire un grand total pour tous

Toutes ces actions peuvent être effectuées par du.

$ du -bs $HOME 2>/dev/null                                                                 
76709521942 /home/xieerqi

Il est intéressant de noter que du a deux "modes" - il peut indiquer combien de fichiers ont une taille OR combien d’espace disque réel qu’ils occupent (la réalité, la réalité physique). Puisque vous êtes intéressé par la taille totale de tous les fichiers, vous voulez la taille apparente du fichier. L'indicateur -b indique exactement que (-b est l'alias de --apparent-size --block-size=1).

Une solution encore plus concise et appropriée serait peut-être d’utiliser du -bc directement sur le répertoire de votre choix. Par exemple, mon répertoire personnel fait environ 76 Go.

$ du -bc $HOME 2> /dev/null  | tail -1                    
76694582570 total

Pour une raison quelconque, vous vous inquiétez de la différence de taille de dossier et de taille de fichier. Vous avez dit dans les commentaires:

Je préférerais ls car la taille des répertoires varie lorsque la taille des fichiers est constante

du est récursif et résume la taille des fichiers. De plus, un répertoire a une taille statique de 4096 octets (4k), mais avec du, il sera inclus dans le résultat de du -bs directory_name. Considère ceci:

$ du -b suse/openSUSE-Leap-42.1-DVD-x86_64.iso                                             
4648337408  suse/openSUSE-Leap-42.1-DVD-x86_64.iso

$ du -b suse/                                                                              
4648341504  suse/

$ bc <<< "4648337408+4096" 
4648341504

$ mkdir suse/another_dir  

$ du -b suse/another_dir                                                                   
4096    suse/another_dir

$ du -bs suse/                                                                             
4648345600  suse/
5

Sous le capot, awk effectue tous les calculs en utilisant des nombres à virgule flottante en double précision. Par défaut, il les imprime avec printf(3) spécificateur de format %.6g, ce qui signifie que si le nombre comporte plus de six chiffres, il basculera sur E-notation , c'est ce que vous avez vu. Vous pouvez contourner ce problème en définissant la variable OFMT:

ls -lR |
    awk 'BEGIN { OFMT = "%d" }  
         /^-/  { total += $5 } 
         END   { print "Total:", total }'

Mais il existe une limite supérieure, au-delà de laquelle il ne peut pas vous donner un nombre exact d'octets; il commencera à arrondir les bits bas de la somme. 500 gigaoctets = 500 * 1024 * 1024 * 1024 = 536870912000 239. Avec la virgule flottante IEEE habituelle, celle-ci est bien en dessous de cette limite (qui est approximativement 252). Cependant, il est suffisamment grand pour que je me sente personnellement mieux à utiliser un langage de programmation doté de "bignums" appropriés (entiers de taille illimitée). Par exemple, Python:

#! /usr/bin/python
import os
import sys

space = 0L  # L means "long" - not necessary in Python 3
for subdir, dirs, files in os.walk(sys.argv[1]):
    for f in files:
        space += os.lstat(os.path.join(subdir, f)).st_size

sys.stdout.write("Total: {:d}\n".format(space))

Ceci est également totalement immunisé contre les problèmes de fichiers contenant des caractères inhabituels dans leurs noms. Et il compte l'espace utilisé par les fichiers cachés.

Ceci calcule le nombre d'octets visibles dans chaque fichier , ce qui correspond à ce que ls -l imprime. Si vous voulez le nombre d'octets réellement occupés sur le disque à la place (ce que du imprime), remplacez .st_size par .st_blocks * 512. (Oui, le multiplicateur est toujours 512, même si st_blksize est un nombre différent.)

4
zwol

Ce que vous voyez ici est un moyen d’afficher de grands nombres. Par exemple:

1.23e+3 = 1.23*10^3 = 1230

Autant que je sache, vous ne pouvez pas désactiver cela, mais comme vous l'avez écrit dans votre question, du se comporte différemment, je vous recommande donc de l'utiliser. Sinon, vous devrez convertir les chiffres.

3
Wayne_Yux