Tragbarer Weg, um die Dateigröße (in Bytes) in der Shell zu erhalten?


121

Unter Linux verwende ich stat --format="%s" FILE, aber Solaris, auf das ich Zugriff habe, hat keinen stat-Befehl. Was soll ich dann verwenden?

Ich schreibe Bash-Skripte und kann keine neue Software auf dem System installieren.

Ich habe darüber nachgedacht, bereits zu verwenden:

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

oder auch:

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

Aber keines davon sieht sinnvoll aus - Perl ausführen, nur um die Dateigröße zu ermitteln? Oder 2 Befehle ausführen, um dasselbe zu tun?


1
Nun, ein Bash-Skript ist Software, und wenn Sie das auf dem System installieren können, können Sie Software installieren.
Nur jemand

4
Technisch - wahr. Ich meinte, dass ich keine Root-Rechte habe und keine neuen Pakete installieren kann. Sicher ist eine Installation im Home-Verzeichnis möglich. Aber nicht wirklich, wenn ich das Skript erstellen muss, das portabel ist, und die Installation auf "X" -Maschinen, werden neue zusätzliche Pakete schwierig.

Antworten:


207

wc -c < filename(Abkürzung für Word Count, -cdruckt die Byteanzahl) ist eine tragbare POSIX- Lösung. Nur das Ausgabeformat ist möglicherweise nicht plattformübergreifend einheitlich, da möglicherweise einige Leerzeichen vorangestellt werden (was bei Solaris der Fall ist).

Lassen Sie die Eingabeumleitung nicht aus. Wenn die Datei als Argument übergeben wird, wird der Dateiname nach der Anzahl der Bytes gedruckt.

Ich hatte Angst, dass es für Binärdateien nicht funktionieren würde, aber es funktioniert sowohl unter Linux als auch unter Solaris einwandfrei. Sie können es mit versuchen wc -c < /usr/bin/wc. Darüber hinaus wird garantiert , dass POSIX-Dienstprogramme Binärdateien verarbeiten , sofern nicht ausdrücklich anders angegeben.


67
Oder nur, wc -c < filewenn der Dateiname nicht angezeigt werden soll.
Café

34
Wenn ich mich aber nicht irre, muss wcin einer Pipeline read()der gesamte Stream die Bytes zählen. Die ls/ awkLösungen (und ähnliche) einen Systemaufruf verwenden , um die Größe zu erhalten, die sollte lineare Zeit ( im Vergleich zu O (Größe))
jmtd

1
Ich erinnere wcmich, dass ich das letzte Mal auf einer vollen Festplatte sehr langsam war. Es war langsam genug, dass ich das Drehbuch neu schreiben konnte, bevor das erste fertig war. Ich kam hierher, um mich daran zu erinnern, wie ich es gemacht habe, lol.
Camilo Martin

6
Ich würde nicht verwenden wc -c; es sieht viel ordentlicher aus, aber ls+ awkist besser für Geschwindigkeit / Ressourcennutzung. Außerdem wollte ich nur darauf hinweisen, dass Sie die Ergebnisse von tatsächlich nachbearbeiten müssen, wcda auf einigen Systemen vor dem Ergebnis Leerzeichen stehen, die Sie möglicherweise entfernen müssen, bevor Sie Vergleiche durchführen können.
Haravikk

3
wc -cist großartig, aber es funktioniert nicht, wenn Sie keinen Lesezugriff auf die Datei haben.
Silas

41

Am Ende habe ich mein eigenes Programm geschrieben (wirklich klein), um nur die Größe anzuzeigen. Weitere Informationen finden Sie hier: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

Die zwei saubersten Möglichkeiten meiner Meinung nach mit gängigen Linux-Tools sind:

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

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

Aber ich möchte einfach keine Parameter eingeben oder die Ausgabe weiterleiten, nur um eine Dateigröße zu erhalten, also verwende ich meine eigene bfsize.


2
In der ersten Zeile der Problembeschreibung heißt es, dass stat keine Option ist und wc -c seit über einem Jahr die beste Antwort ist. Daher bin ich mir nicht sicher, worum es bei dieser Antwort geht.

22
Der Punkt ist , in Menschen wie mich , die diese Frage SO in Google finden und stat ist eine Option für sie.
yo

3
Ich arbeite an einem eingebetteten System, bei dem wc -c4090 ms für eine 10-MB-Datei im Vergleich zu "0" ms benötigt werden. Daher stat -c %sstimme ich zu, dass es hilfreich ist, alternative Lösungen zu haben, auch wenn sie nicht genau die gestellte Frage beantworten.
Robert Calhoun

3
"stat -c" ist nicht portabel / akzeptiert unter MacOS nicht dieselben Argumente wie unter Linux. "wc -c" ist für große Dateien sehr langsam.
Orwellophile

2
stat ist auch nicht portabel. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

Obwohl dunormalerweise die Festplattennutzung und nicht die tatsächliche Datengröße dugedruckt wird , können GNU-Coreutils die "scheinbare Größe" der Datei in Bytes drucken:

du -b FILE

Aber es funktioniert nicht unter BSD, Solaris, macOS, ...


3
Unter MacOS X brew install coreutilsund gdu -bwird den gleichen Effekt erzielen
Jose Alban

1
Ich bevorzuge diese Methode, da wcdas Lesen der gesamten Datei, bevor ein Ergebnis erzielt duwird, sofort erfolgt.
CousinCocaine

2
POSIX erwähnt du -bin einem völlig anderen Kontext in der duBegründung .
Palec

Hierbei wird nur der lstatAufruf verwendet, sodass die Leistung nicht von der Dateigröße abhängt. Kürzer als stat -c '%s', aber weniger intuitiv und funktioniert anders für Ordner (druckt die Größe jeder darin enthaltenen Datei).
Palec

FreeBSDdu kann mit verwendet werden du -A -B1, druckt das Ergebnis jedoch in Vielfachen von 1024B-Blöcken. Hat es nicht geschafft, die Anzahl der Bytes zu drucken. Auch das Einstellen BLOCKSIZE=1in der Umgebung hilft nicht, da dann 512B-Blöcke verwendet werden.
Palec

13

Schließlich entschied ich mich für ls und Bash-Array-Erweiterung:

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

Es ist nicht wirklich schön, aber zumindest macht es nur 1 Fork + Execve und es basiert nicht auf einer sekundären Programmiersprache (Perl / Ruby / Python / was auch immer).


Nur zur Seite - das 'l' in '-ln' ist nicht erforderlich; '-n' ist genau das gleiche wie '-ln'
barryred

Nein, ist es nicht. Vergleichen Sie einfach die Ausgaben.

1
Man könnte vermuten, dass das Portable ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }für den zweiten Schritt der Pipeline keine Verzweigung benötigt, da es nur integrierte Funktionen verwendet, sondern Bash 4.2.37 unter Linux-Gabeln zweimal (allerdings immer noch nur eine execve).
Palec

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"funktioniert mit Single Fork und Single Exec, verwendet jedoch eine temporäre Datei für die Here-Zeichenfolge. Es kann portabel gemacht werden, indem der Here-String durch ein POSX-kompatibles Here-Dokument ersetzt wird . Übrigens beachten Sie die execin der Unterschale. Ohne das führt Bash eine Abzweigung für die Unterschale und eine andere für den darin ausgeführten Befehl aus. Dies ist in dem Code der Fall, den Sie in dieser Antwort angeben. auch.
Palec

1
Das -list in Gegenwart von überflüssig -n. Zitiert von POSIX lsmanpage : -n: Schalten Sie den -l(ell) Option, aber wenn die Akte der Eigentümer oder die Gruppe zu schreiben, schreiben die numerische UID oder GID der Datei statt der Benutzer- oder Gruppennamen, respectively. Deaktivieren Sie die -C, -mund -xOptionen.
Palec

8

Plattformübergreifend schnellste Lösung (verwendet nur single fork () für ls , versucht nicht, tatsächliche Zeichen zu zählen, erzeugt keine nicht benötigten awk, perl usw.).

Unter MacOS, Linux getestet - erfordert möglicherweise geringfügige Änderungen für Solaris:

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

Vereinfachen Sie bei Bedarf die Argumente von ls und passen Sie den Offset in $ {__ ln [3]} an.

Hinweis: folgt Symlinks.


1
Oder setzen Sie es in ein Shell-Skript: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano

1
@Luciano Ich denke, Sie haben den Punkt völlig verpasst, keine Aufgabe in Bash zu erledigen, anstatt Bash zu verwenden, um viele Unix-Befehle auf ineffiziente Weise miteinander zu verbinden.
Orwellophile

8

BSDs haben statmit anderen Optionen als die GNU-Coreutils eine, aber ähnliche Fähigkeiten.

stat -f %z <file name> 

Dies funktioniert unter macOS (getestet am 10.12.), FreeBSD , NetBSD und OpenBSD .


Solaris hat jedoch überhaupt keinen statNutzen.
Palec

6

Bei der Verarbeitung der ls -nAusgabe können Sie als Alternative zu schlecht portierbaren Shell-Arrays die Positionsargumente verwenden, die das einzige Array bilden und die einzigen lokalen Variablen in der Standard-Shell sind. Schließen Sie das Überschreiben von Positionsargumenten in eine Funktion ein, um die ursprünglichen Argumente für Ihr Skript oder Ihre Funktion beizubehalten.

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

Dies teilt die Ausgabe ln -dngemäß den aktuellen IFSEinstellungen der Umgebungsvariablen auf, weist sie Positionsargumenten zu und gibt die fünfte wieder. Die -dsicher Verzeichnisse werden richtig gehandhabt und die -ngewährleistet , dass die Benutzer- und Gruppennamen aufgelöst nicht brauchen werden, im Gegensatz zu -l. Außerdem könnten Benutzer- und Gruppennamen, die Leerzeichen enthalten, theoretisch die erwartete Linienstruktur durchbrechen. Sie sind normalerweise nicht erlaubt, aber diese Möglichkeit lässt den Programmierer immer noch innehalten und nachdenken.


5

Wenn Sie findvon GNU fileutils verwenden:

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

Leider unterstützen andere Implementierungen von findnormalerweise nicht -maxdepth, noch -printf. Dies ist beispielsweise bei Solaris und macOS der Fall find.


FYI maxdepth wird nicht benötigt. Es könnte umgeschrieben werden als size=$(test -f filename && find filename -printf '%s').
Palec

@Palec: Das -maxdepthsoll verhindern, dass findes rekursiv ist (da das, statwas das OP ersetzen muss, nicht ist). Ihr findBefehl fehlt a -nameund der testBefehl ist nicht erforderlich.
Bis auf weiteres angehalten.

@DennisWilliamson finddurchsucht seine Parameter rekursiv nach Dateien, die bestimmten Kriterien entsprechen. Wenn die Parameter keine Verzeichnisse sind, ist die Rekursion… recht einfach. Daher teste ich zuerst, dass filenamees sich wirklich um eine vorhandene gewöhnliche Datei handelt, und finddrucke dann ihre Größe aus, indem ich sie nirgends wiederverwenden kann.
Palec

1
find . -maxdepth 1 -type f -name filename -printf '%s'funktioniert nur, wenn sich die Datei im aktuellen Verzeichnis befindet und möglicherweise weiterhin jede Datei im Verzeichnis überprüft wird, was möglicherweise langsam ist. Besser nutzen (noch kürzer!) find filename -maxdepth 1 -type f -printf '%s'.
Palec

3

Sie können den findBefehl verwenden, um eine Reihe von Dateien abzurufen (hier werden temporäre Dateien extrahiert). Anschließend können Sie mit dem duBefehl die Dateigröße jeder Datei mithilfe von -hswitch in lesbarer Form abrufen.

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

AUSGABE:

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

Ihr erstes Perl-Beispiel erscheint mir nicht unangemessen.

Aus solchen Gründen bin ich vom Schreiben von Shell-Skripten (in Bash / Sh usw.) zum Schreiben aller außer den trivialsten Skripten in Perl übergegangen. Ich stellte fest, dass ich Perl für bestimmte Anforderungen starten musste, und als ich dies immer mehr tat, stellte ich fest, dass das Schreiben der Skripte in Perl wahrscheinlich leistungsfähiger war (in Bezug auf die Sprache und die große Auswahl an Bibliotheken, die über CPAN verfügbar sind) ) und effizienter, um das zu erreichen, was ich wollte.

Beachten Sie, dass andere Shell-Skriptsprachen (z. B. Python / Ruby) zweifellos ähnliche Funktionen haben und Sie diese möglicherweise für Ihre Zwecke bewerten möchten. Ich diskutiere nur über Perl, da dies die Sprache ist, die ich benutze und mit der ich vertraut bin.


Nun, ich schreibe viel Perl selbst, aber manchmal wird das Tool für mich ausgewählt, nicht von mir :)

-3

Wenn Sie Perl auf Ihrem Solaris haben, verwenden Sie es. Andernfalls ist ls mit awk Ihre nächstbeste Wette, da Sie keine Statistik haben oder Ihr Fund kein GNU-Fund ist.


-3

In Solaris gibt es einen Trick, den ich verwendet habe. Wenn Sie nach der Größe von mehr als einer Datei fragen, wird nur die Gesamtgröße ohne Namen zurückgegeben. Fügen Sie daher eine leere Datei wie / dev / null als zweite Datei hinzu:

zB Befehl fileyouwant / dev / null

Ich kann mich nicht erinnern, welcher Größenbefehl für ls / wc / etc funktioniert - leider habe ich keine Solaris-Box zum Testen.


-4

Unter Linux, das Sie verwenden können du -h $FILE, funktioniert das auch unter Solaris?


1
Eigentlich könnten Einheiten konvertiert werden, aber dies zeigt die Festplattennutzung anstelle der Dateidatengröße ("scheinbare Größe").
Palec

-7

Hast du du -ks | ausprobiert? awk '{print $ 1 * 1024}'. Das könnte einfach funktionieren.


1
Dies zeigt die Festplattennutzung anstelle der Dateidatengröße ("scheinbare Größe").
Palec
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.