Por que ls não combinados com o awk mostram tamanhos exatos?

3

Eu estou tentando encontrar o tamanho dos arquivos no meu disco rígido em bytes exatos, mas sempre que o tamanho fica muito grande, o número fica todo estranho (como 1,98329e + 12). Posso impedi-lo de fazer isso ou converter isso em bytes exatos?

O comando é:

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

Imagem dos bytes exatos:

Imagem do número estranho:

  • O ponto de corte antes de parar de exibir bytes exatos parece estar em torno de 500 gb
  • O comando du -sb mostra corretamente os bytes exatos, independentemente do tamanho do diretório.
  • Eu já experimentei o Ubuntu Gnome 15.10 64bit (japonês e inglês) e Linux Mint 17.3 Cinnamon 64bit (japonês)
  • Minhas unidades são ntfs , então tentei formatar uma como ext4 e copiar meus arquivos. Os resultados são os mesmos que os ntfs.
por パンツ 07.04.2016 / 15:38

4 respostas

4

O problema é que o MAWK (a variante AWK instalada no Ubuntu) por padrão imprime números inteiros maiores que 2147483647 (2 31 -1) em notação científica:

% 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 += ; print x}'
2147483647
% printf '2147483648\n' | awk '{x += ; print x}'
2.14748e+09

Você pode usar printf com um especificador de formato em vez de print *:

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

No seu caso:

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

Isso forçará o AWK a imprimir total em notação decimal em vez de em notação científica.

No entanto, em outra nota, você nunca deve analisar ls .

Uma maneira mais sensata de fazer isso seria usar find + stat :

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

* %.0f é um truque para tornar printf números de impressão maiores que 2147483647 (2 31 -1), que usando %d como o especificador de formato sempre seria impresso como %código%. O limite de 2147483647 é que um começará a perder precisão após %.0f (2 53 ), se isso for uma preocupação (graças ao Rotsor para obter informações úteis).

    
por kos 07.04.2016 / 15:57
5

TL; DR : ls e awk são desnecessários para o seu objetivo. Use du -cb ou du -bs no diretório que você deseja analisar.

Seu objetivo é

  1. Encontrar todos os arquivos
  2. encontre seu tamanho (em bytes)
  3. produz o total geral para todos eles

Todas essas ações podem ser realizadas por du .

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

Vale a pena notar que du tem dois "modos" - ele pode mostrar quanto arquivo tem em tamanho OU quanto espaço real em disco ocupa (o real físico). Como você está interessado no tamanho total de todos os arquivos, deseja o tamanho aparente do arquivo. -b flag dá exatamente isso ( -b é alias para --apparent-size --block-size=1 ).

Talvez uma solução ainda mais concisa e apropriada seja usar du -bc diretamente no diretório desejado. Por exemplo, meu diretório pessoal tem cerca de 76 GB de tamanho

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

Por algum motivo, você se preocupa com a diferença no tamanho da pasta e no tamanho do arquivo. Você disse nos comentários:

  

Eu preferiria ls porque os tamanhos dos diretórios variam enquanto os tamanhos dos arquivos são   constante

du é recursivo e resume os tamanhos dos arquivos. Além disso, um diretório tem um tamanho estático de 4096 bytes (4k), mas com du ele será incluído no resultado de du -bs directory_name . Considere isto:

$ 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/
    
por Sergiy Kolodyazhnyy 07.04.2016 / 16:58
4

Sob o capô, awk faz todos os cálculos usando números de ponto flutuante de precisão dupla. Por padrão, imprime usando o printf(3) especificador de formato %.6g , o que significa que se o número tiver mais de seis dígitos largamente, ele mudará para E-notação , que é o que você viu. Você pode contornar isso configurando a variável OFMT :

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

Mas há um limite superior, além do qual não pode dar a você um número exato de bytes; começará a arredondar os bits baixos da soma. 500 gigabytes = 500 * 1024 * 1024 * 1024 = 536870912000 e aprox. 2 39 . Com o ponto flutuante usual do IEEE, isso está seguramente abaixo desse limite (que é aproximadamente 2 52 ). No entanto, é grande o suficiente para que eu me sentisse melhor usando uma linguagem de programação que tivesse "bignums" (números inteiros de tamanho ilimitado). Por exemplo, 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))

Isso também é completamente imune a problemas com arquivos com caracteres incomuns em seus nomes. E conta o espaço consumido por arquivos ocultos.

Isso calcula o número de bytes visíveis em cada arquivo , que é o mesmo que o ls -l imprime. Se você quiser número de bytes realmente ocupados no disco (que du imprime), substitua .st_size por .st_blocks * 512 . (Sim, o multiplicador é sempre 512, mesmo se st_blksize for um número diferente.)

    
por zwol 07.04.2016 / 20:03
3

O que você vê aqui é uma maneira de exibir números grandes. Por exemplo:

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

Até onde eu sei, você não pode desativar isso, mas como escreveu na sua pergunta, du se comporta de maneira diferente, então eu recomendaria usar isso. Caso contrário, você teria que converter os números.

    
por Wayne_Yux 07.04.2016 / 15:47