Modo portatile per ottenere la dimensione del file (in byte) nella shell?


121

Su Linux, utilizzo stat --format="%s" FILE , ma Solaris a cui ho accesso non ha il comando stat. Cosa dovrei usare allora?

Sto scrivendo script Bash e non posso installare alcun nuovo software sul sistema.

Ho già considerato l'utilizzo di:

perl -e '@x=stat(shift);print $x[7]' FILE

o anche:

ls -nl FILE | awk '{print $5}'

Ma nessuno di questi sembra sensato: eseguire Perl solo per ottenere la dimensione del file? O eseguire 2 comandi per fare lo stesso?


1
beh, uno script bash è un software, e se puoi metterlo nel sistema, puoi installare il software.
solo qualcuno il

4
Tecnicamente - vero. Volevo dire che non ho i privilegi di root e non posso installare nuovi pacchetti. Sicuramente l'installazione nella home dir è possibile. Ma non proprio quando devo creare uno script portatile e l'installazione su macchine "X", i nuovi pacchetti aggiuntivi diventano complicati.

Risposte:


207

wc -c < filename(abbreviazione di word count, -cstampa il byte count) è un POSIX portatile soluzione . Solo il formato di output potrebbe non essere uniforme tra le piattaforme poiché alcuni spazi potrebbero essere anteposti (come nel caso di Solaris).

Non omettere il reindirizzamento dell'input. Quando il file viene passato come argomento, il nome del file viene stampato dopo il conteggio dei byte.

Temevo che non funzionasse con i file binari, ma funziona bene sia su Linux che su Solaris. Puoi provarlo con wc -c < /usr/bin/wc. Inoltre, è garantito che le utilità POSIX gestiscano i file binari , se non diversamente specificato in modo esplicito.


67
O solo wc -c < filese non vuoi che il nome del file appaia.
caf

34
Se non sbaglio, però, wcin una pipeline deve read()l'intero flusso per contare i byte. Le soluzioni ls/ awk(e simili) utilizzano una chiamata di sistema per ottenere la dimensione, che dovrebbe essere il tempo lineare (rispetto a O (dimensione))
jmtd

1
Ricordo di wcessere stato molto lento l'ultima volta che l'ho fatto su un disco rigido pieno. È stato abbastanza lento da poter riscrivere la sceneggiatura prima che il primo finisse, sono venuto qui per ricordare come l'ho fatto lol.
Camilo Martin

6
Non userei wc -c; sembra molto più ordinato ma ls+ awkè migliore per la velocità / uso delle risorse. Inoltre, volevo solo sottolineare che in realtà è necessario anche post-elaborare i risultati wcperché su alcuni sistemi avrà spazi bianchi prima del risultato, che potrebbe essere necessario rimuovere prima di poter fare i confronti.
Haravikk

3
wc -cè ottimo, ma non funzionerà se non si dispone dell'accesso in lettura al file.
Silas

41

Ho finito per scrivere il mio programma (molto piccolo) per visualizzare solo le dimensioni. Maggiori informazioni qui: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

I due modi più puliti secondo me con gli strumenti Linux comuni sono:

$ stat -c %s /usr/bin/stat
50000

$ wc -c < /usr/bin/wc
36912

Ma non voglio semplicemente digitare parametri o pipe l'output solo per ottenere una dimensione del file, quindi sto usando il mio bfsize.


2
La prima riga della descrizione del problema afferma che stat non è un'opzione e wc -c è la risposta migliore da oltre un anno, quindi non sono sicuro di quale sia il punto di questa risposta.

22
Il punto è nelle persone come me che trovano questa domanda SO in Google ed stat è un'opzione per loro.
yo'

3
Sto lavorando su un sistema integrato in cui wc -cimpiega 4090 msec su un file da 10 MB rispetto a "0" msec per stat -c %s, quindi sono d'accordo che sia utile avere soluzioni alternative anche quando non rispondono alla domanda esatta posta.
Robert Calhoun

3
"stat -c" non è portabile / non accetta gli stessi argomenti su MacOS come su Linux. "wc -c" sarà molto lento per file di grandi dimensioni.
Orwellophile

2
stat non è nemmeno portatile. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

Anche se di dusolito stampa l'utilizzo del disco e non la dimensione effettiva dei dati, coreutils GNU dupuò stampare la "dimensione apparente" del file in byte:

du -b FILE

Ma non funzionerà con BSD, Solaris, macOS, ...


3
Su MacOS X, brew install coreutilse gdu -botterrà lo stesso effetto
Jose Alban

1
Preferisco questo metodo perché wcnecessita di leggere l'intero file prima di dare un risultato, duè immediato.
CousinCocaine

2
POSIX menziona du -bin un contesto completamente diverso nella dulogica .
Palec

Questo utilizza solo la lstatchiamata, quindi le sue prestazioni non dipendono dalla dimensione del file. Più corto di stat -c '%s', ma meno intuitivo e funziona in modo diverso per le cartelle (stampa la dimensione di ogni file all'interno).
Palec

FreeBSDdu può avvicinarsi usando du -A -B1, ma stampa comunque il risultato in multipli di 1024B blocchi. Non sono riuscito a convincerlo a stampare il conteggio dei byte. Anche l'impostazione BLOCKSIZE=1nell'ambiente non aiuta, perché vengono utilizzati i blocchi 512B.
Palec

13

Alla fine ho deciso di usare ls e l'espansione dell'array bash:

TEMP=( $( ls -ln FILE ) )
SIZE=${TEMP[4]}

non è molto carino, ma almeno fa solo 1 fork + execve, e non si basa su un linguaggio di programmazione secondario (perl / ruby ​​/ python / qualunque cosa)


Solo una parentesi: la "l" in "-ln" non è richiesta; "-n" è esattamente uguale a "-ln"
escluso

No non lo è. Basta confrontare le uscite.

1
Si potrebbe supporre che il portatile ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }non abbia bisogno di fork per il secondo passaggio della pipeline, poiché utilizza solo built-in, ma Bash 4.2.37 su Linux esegue due fork (ancora solo uno execve, però).
Palec

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"funziona con single fork e single exec, ma usa un file temporaneo per la stringa here. Può essere reso portabile sostituendo la stringa here con here-document conforme a POSX . BTW nota il execnella subshell. Senza questo, Bash esegue un fork per la subshell e un altro per il comando in esecuzione all'interno. Questo è il caso del codice che fornisci in questa risposta. pure.
Palec

1
Il -lè superfluo in presenza di -n. Citando POSIX lspagina di manuale : -n: Attivare l' -lopzione (elle), ma quando si scrive il proprietario del file o un gruppo, scrivere UID numerico del file o GID anziché il nome dell'utente o del gruppo, rispettivamente. Disabilitare i -C, -me -xle opzioni.
Palec

8

Soluzione più veloce su più piattaforme (utilizza solo single fork () per ls , non tenta di contare i caratteri effettivi, non genera awk, perl, ecc. Non necessari).

Testato su MacOS, Linux - potrebbe richiedere piccole modifiche per Solaris:

__ln=( $( ls -Lon "$1" ) )
__size=${__ln[3]}
echo "Size is: $__size bytes"

Se necessario, semplifica gli argomenti di ls e regola l'offset in $ {__ ln [3]}.

Nota: seguiranno collegamenti simbolici.


1
Oppure inseriscilo in uno script di shell: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano

1
@Luciano Penso che tu abbia totalmente perso il punto di non biforcare e svolgere un'attività in bash piuttosto che usare bash per mettere insieme molti comandi unix in modo inefficiente.
Orwellophile


6

Durante l'elaborazione ls -ndell'output, in alternativa agli array di shell mal portabili, è possibile utilizzare gli argomenti posizionali, che formano l'unico array e sono le uniche variabili locali nella shell standard. Racchiudi la sovrascrittura degli argomenti posizionali in una funzione per preservare gli argomenti originali nello script o nella funzione.

getsize() { set -- $(ls -dn "$1") && echo $5; }
getsize FILE

Questo divide l'output di in ln -dnbase alle IFSimpostazioni correnti delle variabili d'ambiente, lo assegna agli argomenti posizionali e fa eco al quinto. Le -ddirectory accerta vengono gestiti correttamente e le -nassicura che i nomi di utenti e gruppi non hanno bisogno di essere risolti, a differenza di -l. Inoltre, i nomi di utenti e gruppi contenenti spazi bianchi potrebbero teoricamente rompere la struttura della riga prevista; di solito non sono consentiti, ma questa possibilità fa comunque fermare il programmatore a riflettere.


5

Se usi findda GNU fileutils:

size=$( find . -maxdepth 1 -type f -name filename -printf '%s' )

Sfortunatamente, altre implementazioni di findsolito non supportano -maxdepth, né -printf. Questo è il caso, ad esempio, di Solaris e macOS find.


FYI maxdepth non è necessaria. Potrebbe essere riscritto come size=$(test -f filename && find filename -printf '%s').
Palec

@Palec: ha lo -maxdepthscopo di impedire che findsia ricorsivo (poiché il statche l'OP deve sostituire non lo è). Al tuo findcomando manca una -namee il testcomando non è necessario.
In pausa fino a nuovo avviso.

@DennisWilliamson findricerca i suoi parametri in modo ricorsivo per i file che corrispondono a determinati criteri. Se i parametri non sono directory, la ricorsione è ... abbastanza semplice. Pertanto, prima provo che filenamesia davvero un file ordinario esistente, quindi stampo le sue dimensioni usando findche non ha un posto dove ricorrere.
Palec

1
find . -maxdepth 1 -type f -name filename -printf '%s'funziona solo se il file si trova nella directory corrente e può ancora esaminare ogni file nella directory, il che potrebbe essere lento. Migliore utilizzo (ancora più breve!) find filename -maxdepth 1 -type f -printf '%s'.
Palec

3

È possibile utilizzare il findcomando per ottenere una serie di file (qui vengono estratti i file temporanei). Quindi puoi usare il ducomando per ottenere la dimensione del file di ogni file in forma leggibile dall'uomo usando l' -hopzione.

find $HOME -type f -name "*~" -exec du -h {} \;

PRODUZIONE:

4.0K    /home/turing/Desktop/JavaExmp/TwoButtons.java~
4.0K    /home/turing/Desktop/JavaExmp/MyDrawPanel.java~
4.0K    /home/turing/Desktop/JavaExmp/Instream.java~
4.0K    /home/turing/Desktop/JavaExmp/RandomDemo.java~
4.0K    /home/turing/Desktop/JavaExmp/Buff.java~
4.0K    /home/turing/Desktop/JavaExmp/SimpleGui2.java~

2

Il tuo primo esempio di Perl non mi sembra irragionevole.

È per ragioni come questa che sono passato dalla scrittura di script di shell (in bash / sh ecc.) Alla scrittura di tutti gli script tranne quelli più banali in Perl. Ho scoperto che dovevo lanciare Perl per esigenze particolari, e mentre lo facevo sempre di più, mi sono reso conto che scrivere gli script in Perl era probabilmente un modo più potente (in termini di linguaggio e l'ampia gamma di librerie disponibili tramite CPAN ) e un modo più efficiente per ottenere ciò che volevo.

Si noti che altri linguaggi di scripting della shell (ad esempio python / ruby) avranno senza dubbio strutture simili, e potresti volerle valutare per i tuoi scopi. Parlo solo di Perl poiché è il linguaggio che uso e con cui ho familiarità.


Bene, scrivo molto in Perl da solo, ma a volte lo strumento viene scelto per me, non da me :)

-3

se hai Perl su Solaris, usalo. Altrimenti, ls con awk è la tua prossima migliore scommessa, poiché non hai stat o la tua ricerca non è GNU find.


-3

C'è un trucco in Solaris che ho usato, se chiedi la dimensione di più di un file restituisce solo la dimensione totale senza nomi, quindi includi un file vuoto come / dev / null come secondo file:

ad esempio file di comando che desideri / dev / null

Non riesco a ricordare quale comando di dimensione funzioni per ls / wc / etc - sfortunatamente non ho una scatola Solaris per provarlo.


-4

su Linux si può usare du -h $FILE, funziona anche su Solaris?


1
In realtà, le unità potrebbero essere convertite, ma questo mostra l'utilizzo del disco invece della dimensione dei dati del file ("dimensione apparente").
Palec

-7

Hai provato du -ks | awk "{print $ 1 * 1024}". Potrebbe funzionare.


1
Questo mostra l'utilizzo del disco invece della dimensione dei dati del file ("dimensione apparente").
Palec
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.