Come posso ottenere la dimensione di un file in uno script bash?


Risposte:


242

La tua scommessa migliore se su un sistema GNU:

stat --printf="%s" file.any

Da man stat :

% s dimensione totale, in byte

In uno script bash:

#!/bin/bash
FILENAME=/home/heiko/dummy/packages.txt
FILESIZE=$(stat -c%s "$FILENAME")
echo "Size of $FILENAME = $FILESIZE bytes."

NOTA: vedi la risposta di @ chbrown per come utilizzare stat nel terminale su Mac OS X.


7
@ haunted85 statè il modo più semplice, supponendo che tu stia usando Linux o Cygwin ( statnon è standard). wc -ccome suggerito da Eugéne è portatile.
Gilles,

2
stat: illegal option -- c
Iulian Onofrei,

stat --printf="%s" file.txtnon produce nulla su Debian Jessie ...
woohoo,

5
Su MacOS funziona:stat -f%z myfile.tar
ccpizza

2
@woohoo Il tuo prompt sovrascrive l'output. man statdice che --printf omette la nuova riga finale. Utilizzare --formato -cper vedere l'output. Ottieni maggiori informazioni comparando stat --printf="%s" file.any | xxd -astat -c "%s" file.any | xxd -
nipote il

92
file_size_kb=`du -k "$filename" | cut -f1`

Il problema con l'utilizzo statè che si tratta di un'estensione GNU (Linux). du -ke cut -f1sono specificati da POSIX e sono quindi portabili su qualsiasi sistema Unix.

Solaris, ad esempio, viene fornito con bash ma non con stat. Quindi questo non è del tutto ipotetico.

lspresenta un problema simile in quanto non viene specificato il formato esatto dell'output, pertanto l'analisi del suo output non può essere eseguita in modo portabile. du -hè anche un'estensione GNU.

Attenersi ai costrutti portatili, ove possibile, e semplificherete la vita di qualcuno in futuro. Forse il tuo.


48
dunon fornisce le dimensioni del file, fornisce un'indicazione di quanto spazio utilizza il file, che è leggermente diverso (di solito la dimensione indicata da duè la dimensione del file arrotondata per eccesso al numero più vicino di blocchi, dove un blocco è in genere 512B o 1kB o 4kB).
Gilles,

7
@Gilles, i file sparsi (cioè quelli con buchi) riportano meno della lunghezza.
vonbrand,

5
Questa, con --byteso -binvece di -k, dovrebbe essere la risposta accettata.
Amedee Van Gasse,

1
L' -hopzione ("umana")du produrrà la risposta più appropriata per i casi generali:, file_size=`du -h "$filename" | cut -f1poiché visualizzerà K (kilobyte), M (Megabyte) o G (Gigabyte) come appropriato.
fralau

1
@fralau: L'OP vuole "assegnare questo a una variabile bash in modo che possano usarlo in seguito", quindi è molto più probabile che vogliano un valore numerico effettivo, non un'approssimazione leggibile dall'uomo. Inoltre, -hè un'estensione GNU; non è standard
Nemo

74

Puoi anche usare il comando "conteggio parole" ( wc):

wc -c "$filename" | awk '{print $1}'

Il problema wcè che aggiungerà il nome file e indenterà l'output. Per esempio:

$ wc -c somefile.txt
    1160 somefile.txt

Se desideri evitare il concatenamento di una lingua completamente interpretata o di un editor di stream solo per ottenere un conteggio delle dimensioni del file, reindirizza l'input dal file in modo che wcnon veda mai il nome del file:

wc -c < "$filename"

Quest'ultimo modulo può essere usato con la sostituzione dei comandi per afferrare facilmente il valore che stavi cercando come variabile di shell, come menzionato da Gilles di seguito.

size="$(wc -c <"$filename")"

30
wc -c <"$FILENAME"dà le dimensioni senza nessun'innesto, quindi size=$(wc -c <"$FILENAME").
Gilles,

6
Ancora un altro punto: l'ho appena testato e wc -c < filesembra essere molto veloce, almeno su OS X. Immagino che wc abbia il cervello per provare a stat il file se viene specificato solo -c.
Edward Falk,

4
@EdwardFalk: wc -cutilizza GNU fstat, ma cerca il penultimo blocco del file e legge gli ultimi st_blksizebyte fino a . Apparentemente ciò è dovuto al fatto che i file in Linux /proce, /sysad esempio, hanno dimensioni stat solo approssimative e wcvogliono riportare la dimensione effettiva, non la dimensione riportata dalle statistiche. Immagino che sarebbe strano wc -criportare una dimensione diversa da quella wc, ma non è idea leggere i dati dal file se è un normale file su disco e non è in memoria. O peggio, archiviazione su nastro near-line ...
Peter Cordes,

1
Sembra che printfveda ancora il rientro, ad es. printf "Size: $size"-> size: <4 spaces> 54339. D'altra parte echoignora lo spazio bianco. Un modo per renderlo coerente?
Eugene Kulabuhov,

2
@keithpjolley: chiamando fstat. Prova a correre strace wc -c </etc/passwde puoi vedere cosa sta facendo.
Nemo

48

BSD (Mac OS X) statha un flag di argomento di formato diverso e diversi identificatori di campo. Da man stat(1):

  • -f format: Visualizza le informazioni utilizzando il formato specificato. Vedi la sezione FORMATI per una descrizione dei formati validi.
  • ... la sezione FORMATI ...
  • z: La dimensione del file in byte.

Quindi tutti insieme ora:

stat -f%z myfile1.txt

28

Dipende da cosa intendi per dimensione .

size=$(wc -c < "$file")

ti darà il numero di byte che possono essere letti dal file. IOW, è la dimensione del contenuto del file. Tuttavia leggerà il contenuto del file (tranne se il file è un file normale o un link simbolico al file normale nella maggior parte delle wcimplementazioni come ottimizzazione). Ciò potrebbe avere effetti collaterali. Ad esempio, per una pipe denominata, ciò che è stato letto non può più essere letto di nuovo e per cose come /dev/zeroo /dev/randomche sono di dimensioni infinite, ci vorrà del tempo. Ciò significa anche che è necessaria l' readautorizzazione per il file e potrebbe essere aggiornato l' ultimo timestamp di accesso al file.

È standard e portatile, tuttavia si noti che alcune wcimplementazioni possono includere spazi vuoti iniziali in tale output. Un modo per sbarazzarsi di loro è usare:

size=$(($(wc -c < "$file")))

o per evitare un errore su un'espressione aritmetica vuota in dasho yashquando wcnon produce alcun output (come quando il file non può essere aperto):

size=$(($(wc -c < "$file") +0))

ksh93ha wcincorporato (purché tu lo abiliti, puoi anche invocarlo come command /opt/ast/bin/wc) che lo rende il più efficiente per i file regolari in quella shell.

Vari sistemi hanno un comando chiamato statche è un'interfaccia per le stat()o lstat()chiamate di sistema.

Quelle riportano le informazioni trovate nell'inode. Una di queste informazioni è l' st_sizeattributo. Per i file regolari, questa è la dimensione del contenuto (quanti dati possono essere letti da esso in assenza di errori (questo è ciò che la maggior parte delle wc -cimplementazioni utilizza nella loro ottimizzazione)). Per i collegamenti simbolici, questa è la dimensione in byte del percorso target. Per le pipe con nome, a seconda del sistema, è 0 o il numero di byte attualmente nel buffer delle pipe. Lo stesso vale per i dispositivi a blocchi in cui, a seconda del sistema, si ottiene 0 o la dimensione in byte della memoria sottostante.

Non è necessario il permesso di lettura del file per ottenere tali informazioni, solo il permesso di ricerca nella directory a cui è collegato.

Per ordine cronologico, c'è:

  • IRIXstat (anni '90):

    stat -qLs -- "$file"

    restituisce l' st_sizeattributo di $file( lstat()) o:

    stat -s -- "$file"

    lo stesso tranne quando $fileè un collegamento simbolico nel qual caso è st_sizeil file dopo la risoluzione del collegamento simbolico.

  • zsh statbuiltin (ora noto anche come zstat) nel zsh/statmodulo (caricato con zmodload zsh/stat) (1997):

    stat -L +size -- $file # st_size of file
    stat +size -- $file    # after symlink resolution
    

    o per memorizzare in una variabile:

    stat -L -A size +size -- $file

    ovviamente, questo è il più efficiente in quella shell.

  • GNUstat (2001); anche in BusyBox statdal 2005 (copiato da GNU stat):

    stat -c %s -- "$file"  # st_size of file
    stat -Lc %s -- "$file" # after symlink resolution
    

    (notare che il significato di -Lè invertito rispetto a IRIX o zsh stat.

  • BSDstat (2002):

    stat -f %z -- "$file"  # st_size of file
    stat -Lf %z -- "$file" # after symlink resolution
    

Oppure puoi usare la funzione stat()/ lstat()di alcuni linguaggi di scripting come perl:

perl -le 'print((lstat shift)[7])' -- "$file"

AIX ha anche un istatcomando che scaricherà tutte le informazioni stat()(no lstat(), quindi non funzionerà sui collegamenti simbolici) e con le quali potresti post-processarle, ad esempio:

LC_ALL=C istat "$file" | awk 'NR == 4 {print $5}'

(grazie @JeffSchaller per l' aiuto nella comprensione dei dettagli ).

In tcsh:

@ size = -Z $file:q

(dimensioni dopo la risoluzione del collegamento simbolico)

Molto prima che GNU introducesse il suo statcomando, lo stesso poteva essere ottenuto con il findcomando GNU con il suo -printfpredicato (già nel 1991):

find -- "$file" -prune -printf '%s\n'    # st_size of file
find -L -- "$file" -prune -printf '%s\n' # after symlink resolution

Un problema però è che non funziona se $fileinizia con -o è un findpredicato (come !, (...).

Il comando standard per ottenere le informazioni stat()/ lstat()è ls.

POSIXly, puoi fare:

LC_ALL=C ls -dn -- "$file" | awk '{print $5; exit}'

e aggiungere -Lper lo stesso dopo la risoluzione del collegamento simbolico. Ciò non funziona per i file di dispositivo, sebbene il 5 ° campo sia il numero maggiore del dispositivo anziché la dimensione.

Per i dispositivi a blocchi, i sistemi in cui stat()restituisce 0 per st_size, di solito hanno altre API per segnalare la dimensione del dispositivo a blocchi. Ad esempio, Linux ha BLKGETSIZE64 ioctl(), e la maggior parte delle distribuzioni Linux ora viene fornita con un blockdevcomando che può farne uso:

blockdev --getsize64 -- "$device_file"

Tuttavia, è necessario disporre dell'autorizzazione di lettura per il file del dispositivo. Di solito è possibile ricavare la dimensione con altri mezzi. Ad esempio (ancora su Linux):

lsblk -bdno size -- "$device_file"

Dovrebbe funzionare ad eccezione dei dispositivi vuoti.

Un approccio che funziona per tutti i file ricercabili (quindi include file regolari, la maggior parte dei dispositivi a blocchi e alcuni dispositivi a caratteri) è quello di aprire il file e cercare fino alla fine:

  • Con zsh(dopo aver caricato il zsh/systemmodulo):

    {sysseek -w end 0 && size=$((systell(0)))} < $file
  • Con ksh93:

    < "$file" <#((size=EOF))

    o

    { size=$(<#((EOF))); } < "$file"
  • con perl:

    perl -le 'seek STDIN, 0, 2 or die "seek: $!"; print tell STDIN' < "$file"

Per le pipe con nome, abbiamo visto che alcuni sistemi (almeno AIX, Solaris, HP / UX) rendono disponibile la quantità di dati nel buffer delle pipe in stat()'s st_size. Alcuni (come Linux o FreeBSD) no.

Almeno su Linux, puoi usare il FIONREAD ioctl()dopo aver aperto la pipe (in modalità lettura + scrittura per evitare che si blocchi):

fuser -s -- "$fifo_file" && 
  perl -le 'require "sys/ioctl.ph";
            ioctl(STDIN, &FIONREAD, $n) or die$!;
            print unpack "L", $n' <> "$fifo_file"

Tuttavia, nota che mentre non legge il contenuto della pipa, la semplice apertura della pipa denominata qui può comunque avere effetti collaterali. Stiamo usando fuserper verificare innanzitutto che alcuni processi abbiano già aperto il tubo per alleviarlo, ma questo non è infallibile in quanto fuserpotrebbe non essere in grado di controllare tutti i processi.

Ora, finora abbiamo considerato solo la dimensione dei dati primari associati ai file. Ciò non tiene conto delle dimensioni dei metadati e di tutte le infrastrutture di supporto necessarie per archiviare quel file.

Un altro attributo inode restituito da stat()è st_blocks. Questo è il numero di blocchi da 512 byte utilizzati per archiviare i dati del file (e talvolta alcuni dei suoi metadati come gli attributi estesi su filesystem ext4 su Linux). Ciò non include l'inode stesso o le voci nelle directory a cui è collegato il file.

Le dimensioni e l'utilizzo del disco non sono necessariamente strettamente correlati come compressione, scarsità (a volte alcuni metadati), infrastrutture extra come blocchi indiretti in alcuni filesystem hanno un'influenza su quest'ultimo.

Questo è in genere ciò che viene duutilizzato per segnalare l'utilizzo del disco. La maggior parte dei comandi sopra elencati sarà in grado di ottenere tali informazioni.

  • POSIXLY_CORRECT=1 ls -sd -- "$file" | awk '{print $1; exit}'
  • POSIXLY_CORRECT=1 du -s -- "$file" (non per le directory in cui ciò includerebbe l'utilizzo del disco dei file all'interno).
  • GNU find -- "$file" -printf '%b\n'
  • zstat -L +block -- $file
  • GNU stat -c %b -- "$file"
  • BSD stat -f %b -- "$file"
  • perl -le 'print((lstat shift)[12])' -- "$file"

chiaramente la risposta più completa e informativa. grazie. posso usarlo per creare script bash multipiattaforma usando le informazioni sulle statistiche BSD e GNU
oligofren,

1
Curiosità: GNU coreutils wc -cusa fstat, ma poi legge gli ultimi st_blksizebyte fino a . Apparentemente ciò è dovuto al fatto che i file in Linux /proce, /sysad esempio, hanno dimensioni stat solo approssimative . Questo è utile per la correttezza, ma male se la fine del file è su disco e non in memoria (specialmente se usata su molti file in un ciclo). E molto male se il file viene migrato su un archivio su nastro near-line o, ad esempio, un filesystem a decompressione trasparente FUSE.
Peter Cordes,

non funzionerebbe anche questols -go file | awk '{print $3}'
Steven Penny,

@StevenPenny quelli -gosarebbero quelli SysV, non funzionerebbero su BSD (opzionale (XSI) in POSIX). Avresti anche bisogno ls -god file | awk '{print $3; exit}'( -daffinché funzioni su directory, exitper collegamenti simbolici con le nuove righe nel target). Restano anche i problemi con i file del dispositivo.
Stéphane Chazelas,

1
@ αғsнιη l'API Unix non fa distinzione tra file di testo e file binari. Sono tutte sequenze di byte. Alcune applicazioni potrebbero voler interpretare quei byte come testo ma ovviamente non wc -criportano il numero di byte.
Stéphane Chazelas,

22

Questo script combina molti modi per calcolare la dimensione del file:

(
  du --apparent-size --block-size=1 "$file" 2>/dev/null ||
  gdu --apparent-size --block-size=1 "$file" 2>/dev/null ||
  find "$file" -printf "%s" 2>/dev/null ||
  gfind "$file" -printf "%s" 2>/dev/null ||
  stat --printf="%s" "$file" 2>/dev/null ||
  stat -f%z "$file" 2>/dev/null ||
  wc -c <"$file" 2>/dev/null
) | awk '{print $1}'

Lo script funziona su molti sistemi Unix tra cui Linux, BSD, OSX, Solaris, SunOS, ecc.

La dimensione del file mostra il numero di byte. È la dimensione apparente, ovvero i byte che il file utilizza su un disco tipico, senza compressione speciale, aree speciali sparse, blocchi non allocati, ecc.

Questo script ha una versione di produzione con più aiuto e più opzioni qui: https://github.com/SixArm/file-size


9

stat sembra farlo con il minor numero di chiamate di sistema:

$ set debian-live-8.2.0-amd64-xfce-desktop.iso

$ strace stat --format %s $1 | wc
    282    2795   27364

$ strace wc --bytes $1 | wc
    307    3063   29091

$ strace du --bytes $1 | wc
    437    4376   41955

$ strace find $1 -printf %s | wc
    604    6061   64793

8

ls -l filename ti fornirà molte informazioni su un file, incluse le dimensioni del file, le autorizzazioni e il proprietario.

La dimensione del file nella quinta colonna e viene visualizzata in byte. Nell'esempio seguente, la dimensione del file è appena inferiore a 2 KB:

-rw-r--r-- 1 user owner 1985 2011-07-12 16:48 index.php

Modifica: apparentemente non è affidabile come il statcomando.


Penso che entrambi ls -le il statcomando forniscano informazioni sulle dimensioni affidabili. Non ho trovato alcun riferimento al contrario. ls -sdarà dimensioni in numero di blocchi.
dabest1,

2
@ dabest1 non è affidabile nel senso che in un altro unix, il loro output può essere diverso (e in alcuni unix lo è).
Eugene Bujak,

Sì, IIRC, Solaris non visualizzava il nome del gruppo per impostazione predefinita, portando a un numero inferiore di colonne nell'output.
Edward Falk,

Poiché la dimensione è numerica pura, circondata da spazi bianchi e l'anno della data è numerico puro, in un formato definito, sarebbe possibile utilizzare una regexp per trattare utente + proprietario come un campo, indipendentemente dalla presenza del gruppo. (un esercizio per il lettore!)
MikeW

5

du filename ti dirà l'utilizzo del disco in byte.

Preferisco du -h filename, che ti dà le dimensioni in un formato leggibile dall'uomo.


2
che o stat -c "%s";)

1
Questo tipo di dustampa stampa la dimensione in blocchi di 1024 byte, non un semplice conteggio di byte.
Peter Lyons,

Si noti che lo standard dufornisce un'uscita in numero di unità da 512 byte. GNU duusa invece i kibibyte a meno che non sia chiamato con POSIXLY_CORRECTnel suo ambiente.
Stéphane Chazelas,

1
Per i file di tipo directory , ciò fornisce l'utilizzo del disco della directory ma anche di tutti gli altri file all'interno (ricorsivamente).
Stéphane Chazelas,

3

Crea piccole funzioni di utilità negli script della shell a cui puoi delegare.

Esempio

#! /bin/sh -
# vim: set ft=sh

# size utility that works on GNU and BSD systems
size(){
    case $(uname) in
        (Darwin | *BSD*)
            stat -Lf %z -- "$1";;
        (*) stat -c %s -- "$1"
    esac
}

for f do
    printf '%s\n' "$f : $(gzip < "$f" | wc -c) bytes (versus $(size "$f") bytes)"
done

Sulla base di informazioni fornite dalla risposta di @ Stéphane Chazelas.


Vedi anche gzip -v < file > /dev/nullper verificare la compressibilità di un file.
Stéphane Chazelas,

@ StéphaneChazelas non sono sicuro se penso che sia stato un miglioramento. quelle dichiarazioni di casi possono facilmente rimandare; Di certo non ricordo mai come farli nel modo giusto :-) le dichiarazioni dei casi sono intrinsecamente più portatili da quando l'hai fatto? vedo il punto in cui ci sono più di due casi, ma per il resto ... +
oligofren,

1
Suppongo sia anche una questione di gusti, ma qui è il caso tipico in cui vorresti usare una casedichiarazione. caseè il costrutto Bourne / POSIX per eseguire la corrispondenza dei modelli. [[...]]è solo ksh / bash / zsh (con variazioni).
Stéphane Chazelas,

2

Ho trovato un Liner AWK 1 e aveva un bug ma l'ho risolto. Ho anche aggiunto PetaBytes dopo TeraBytes.

FILE_SIZE=234234 # FILESIZE IN BYTES
FILE_SIZE=$(echo "${FILE_SIZE}" | awk '{ split( "B KB MB GB TB PB" , v ); s=1; while( $1>1024 ){ $1/=1024; s++ } printf "%.2f %s", $1, v[s] }')

Considerando che stat non è presente su ogni singolo sistema, è quasi sempre possibile utilizzare la soluzione AWK. Esempio; il Raspberry Pi non ha stat ma ha awk .


1
Completamente NON quello che l'OP ha chiesto, ma un bel piccolo pezzo di lavoro.
Gypsy Spellweaver,

0

Un altro modo conforme a POSIX sarebbe utilizzare awkcon la sua length()funzione che restituisce la lunghezza, in caratteri su ciascuna riga del file di input, esclusi i caratteri di nuova riga. Così facendo

awk '{ sum+=length } END { print sum+NR }' file

ci assicuriamo che NRvenga aggiunto sum, risultando quindi nel conteggio totale dei caratteri e nel numero totale di nuove righe incontrate nel file. La length()funzione in awkaccetta un argomento che per impostazione predefinita significa length($0)che è per l'intera riga corrente.


Non se l'ultima riga non termina con una nuova riga: printf 'a\nb' | awk '{ sum+=length } END { print sum+NR }'dovrebbe stampare 3 ma stampare 4.
Isaac il

-1

Mi piace anche l'opzione WC. Abbinato a "bc", puoi ottenere decimali in tutti i posti che desideri.

Stavo cercando di migliorare uno script che avevo strappato fuori dalla colonna 'dimensione file' di un comando 'ls -alh'. Non volevo solo dimensioni di file interi e due decimali sembravano adattarsi, quindi dopo aver letto questa discussione, ho trovato il codice qui sotto.

Suggerisco di spezzare la linea al punto e virgola se includi questo in uno script.

file=$1; string=$(wc -c $file); bite=${string% *}; okay=$(echo "scale=2; $bite/1024" | bc);friend=$(echo -e "$file $okay" "kb"); echo -e "$friend"

Il mio script si chiama gpfl , per "ottenere la lunghezza del file di immagine". Lo uso dopo aver fatto un mogrify su un file in imagemagick, prima di aprire o ricaricare un'immagine in un visualizzatore jpeg GUI.

Non so come questa sia una "risposta", poiché prende in prestito molto da ciò che è già stato offerto e discusso. Quindi lo lascerò lì.

BZT


1
Preferirei usare "stat" o "ls". In genere non mi piace usare "wc" per ottenere le dimensioni dei file perché legge fisicamente l'intero file. Se hai molti file, o file particolarmente grandi, questo può richiedere molto tempo. Ma la tua soluzione è creativa ... + 1.
Kevin Fegan,

2
Sono d'accordo con l'idea di utilizzare "stat" su "wc" per dimensione file, tuttavia se si utilizza "wc -c", i dati non verranno letti; invece lseek verrà utilizzato per capire il numero di byte in un file. lingrok.org/xref/coreutils/src/wc.c#228
bbaja42

1
@ bbaja42: nota che GNU Coreutils wclegge l'ultimo blocco del file, nel caso stat.st_sizefosse solo un'approssimazione (come per Linux /proce i /sysfile). Immagino che abbiano deciso di non rendere il commento principale più complicato quando hanno aggiunto quella logica un paio di righe: lingrok.org/xref/coreutils/src/wc.c#246
Peter Cordes,

-1

Il metodo più rapido e semplice (IMO) è:

bash_var=$(stat -c %s /path/to/filename)

2
Quindi vota una o più delle risposte esistenti che menzionano stat; non c'è bisogno di ripeterlo di nuovo ...
Jeff Schaller

1
@JeffSchaller Ho appena votato la risposta di Stephane sulle tue istruzioni. Penso che sia troppo complicato per i miei scopi. Ecco perché ho pubblicato questa semplice risposta per anime affini.
WinEunuuchs2Unix

1
Grazie; è solo che una sesta istanza di una risposta "stat" non semplifica questa domanda e risposta, ma preferirebbe far chiedere a un nuovo lettore "in che modo questa risposta è diversa dalle altre?" e porta a più confusione anziché a meno.
Jeff Schaller

@JeffSchaller credo. Ma potrei lamentarmi delle molte due delle wcrisposte che dovrebbero avere una dichiarazione di non responsabilità MAI FARE QUESTO nella vita reale. Stasera ho usato la mia risposta in un'applicazione reale e ho pensato che valesse la pena condividerla. Immagino che tutti noi abbiamo le nostre opinioni scrollate di spalle .
WinEunuuchs2Unix
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.