Un moyen portable pour obtenir la taille du fichier (en octets) dans le shell?

121

Sous Linux, j'utilise stat --format="%s" FILE , mais Solaris auquel j'ai accès n'a pas de commande stat. Que dois-je utiliser alors?

J'écris des scripts Bash et je ne peux pas vraiment installer de nouveau logiciel sur le système.

J'ai déjà envisagé d'utiliser:

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

ou même:

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

Mais ni l'un ni l'autre ne semble raisonnable - exécuter Perl juste pour obtenir la taille du fichier? Ou exécuter 2 commandes pour faire la même chose?


la source
1
eh bien, un script bash est un logiciel, et si vous pouvez le mettre sur le système, vous pouvez installer un logiciel.
juste quelqu'un
4
Techniquement - vrai. Je voulais dire que je n'ai pas de privilèges root et que je ne peux pas installer de nouveaux packages. Bien sûr, l'installation dans le répertoire personnel est possible. Mais pas vraiment quand je dois rendre le script portable, et l'installation sur des machines "X", de nouveaux paquets supplémentaires deviennent délicats.

Réponses:

207

wc -c < filename(abréviation de word count, -cimprime le nombre d'octets) est une solution POSIX portable . Seul le format de sortie peut ne pas être uniforme sur toutes les plates-formes car certains espaces peuvent être ajoutés au début (ce qui est le cas pour Solaris).

N'omettez pas la redirection d'entrée. Lorsque le fichier est passé en argument, le nom du fichier est imprimé après le nombre d'octets.

J'avais peur que cela ne fonctionne pas pour les fichiers binaires, mais cela fonctionne bien sur Linux et Solaris. Vous pouvez l'essayer avec wc -c < /usr/bin/wc. De plus, les utilitaires POSIX sont garantis pour gérer les fichiers binaires , sauf indication contraire explicite.

Carl Smotricz
la source
67
Ou simplement wc -c < filesi vous ne voulez pas que le nom du fichier apparaisse.
caf
34
Si je ne me trompe pas, cependant, wcdans un pipeline, read()le flux entier doit compter les octets. Les ls/ awksolutions (et similaires) utilisent un appel système pour obtenir la taille, qui devrait être le temps linéaire (par rapport à O (taille))
jmtd
1
Je me souviens wcavoir été très lent la dernière fois que j'ai fait ça sur un disque dur plein. C'était assez lent pour que je puisse réécrire le script avant que le premier ne soit terminé, je suis venu ici pour me rappeler comment je l'ai fait lol.
Camilo Martin
6
Je n'utiliserais pas wc -c; il a l'air beaucoup plus soigné mais ls+ awkest meilleur pour l'utilisation de la vitesse / des ressources. De plus, je voulais juste souligner que vous devez également post-traiter les résultats, wccar sur certains systèmes, il y aura des espaces avant le résultat, que vous devrez peut-être supprimer avant de pouvoir faire des comparaisons.
Haravikk
3
wc -cc'est génial, mais cela ne fonctionnera pas si vous n'avez pas d'accès en lecture au fichier.
Silas
41

J'ai fini par écrire mon propre programme (vraiment petit) pour n'afficher que la taille. Plus d'informations ici: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

Les deux moyens les plus propres à mon avis avec les outils Linux courants sont:

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

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

Mais je ne veux tout simplement pas taper des paramètres ou diriger la sortie uniquement pour obtenir une taille de fichier, donc j'utilise ma propre taille bfs.

fwhacking
la source
2
La première ligne de description du problème indique que stat n'est pas une option, et le wc -c est la meilleure réponse depuis plus d'un an maintenant, donc je ne suis pas sûr de l'intérêt de cette réponse.
22
Le point est dans les gens comme moi qui trouvent cette question SO dans Google et stat est une option pour eux.
le
3
Je travaille sur un système embarqué où wc -cprend 4090 msec sur un fichier de 10 Mo contre "0" msec pour stat -c %s, donc je conviens qu'il est utile d'avoir des solutions alternatives même si elles ne répondent pas exactement à la question posée.
Robert Calhoun
3
"stat -c" n'est pas portable / n'accepte pas les mêmes arguments sur MacOS que sur Linux. "wc -c" sera très lent pour les gros fichiers.
Orwellophile
2
stat n'est pas non plus portable. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]
27

Même s'il duimprime généralement l'utilisation du disque et non la taille réelle des données, GNU coreutils dupeut imprimer la "taille apparente" du fichier en octets:

du -b FILE

Mais cela ne fonctionnera pas sous BSD, Solaris, macOS, ...

fwhacking
la source
3
Sur MacOS X, brew install coreutilset gdu -bobtiendra le même effet
Jose Alban
1
Je préfère cette méthode car le wcbesoin de lire tout le fichier avant de donner un résultat duest immédiat.
CousinCocaine
2
POSIX mentionne du -bdans un tout autre contexte de dujustification .
Palec
Cela utilise uniquement l' lstatappel, donc ses performances ne dépendent pas de la taille du fichier. Plus court que stat -c '%s', mais moins intuitif et fonctionne différemment pour les dossiers (imprime la taille de chaque fichier à l'intérieur).
Palec
FreeBSDdu peut se rapprocher en utilisant du -A -B1, mais il imprime toujours le résultat en multiples de blocs de 1024B. N'a pas réussi à le faire imprimer le nombre d'octets. Même la configuration BLOCKSIZE=1dans l'environnement n'aide pas, car le bloc 512B est alors utilisé.
Palec
13

Enfin, j'ai décidé d'utiliser ls et l'extension de tableau bash:

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

ce n'est pas vraiment sympa, mais au moins il ne fait qu'un fork + execve, et il ne repose pas sur un langage de programmation secondaire (perl / ruby ​​/ python / peu importe)


la source
Juste un aparté - le «l» dans «-ln» n'est pas nécessaire; '-n' est exactement le même que '-ln'
barryred
Non ce n'est pas. Comparez simplement les sorties.
1
On pourrait supposer que le portable ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }n'a pas besoin de fork pour la deuxième étape du pipeline, car il n'utilise que des composants intégrés, mais Bash 4.2.37 sur Linux forks deux fois (toujours un seul execve, cependant).
Palec le
read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"fonctionne avec un seul fork et un seul exec, mais il utilise un fichier temporaire pour la chaîne here-string. Il peut être rendu portable en remplaçant la chaîne here-string par here-document compatible POSX . BTW notez le execdans le sous-shell. Sans cela, Bash effectue un fork pour le sous-shell et un autre pour la commande exécutée à l'intérieur. C'est le cas dans le code que vous fournissez dans cette réponse. aussi.
Palec
1
Le -lest superflu en présence de -n. Citant la lspage de manuel POSIX :: activez -nl' -loption (ell), mais lors de l'écriture du propriétaire ou du groupe du fichier, écrivez respectivement l'UID ou le GID numérique du fichier plutôt que le nom de l'utilisateur ou du groupe. Désactivez les -C, -met les -xoptions.
Palec
8

Solution la plus rapide multiplateforme (utilise uniquement un seul fork () pour ls , n'essaie pas de compter les caractères réels, ne génère pas de awk, perl, etc. inutiles).

Testé sur MacOS, Linux - peut nécessiter des modifications mineures pour Solaris:

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

Si nécessaire, simplifiez les arguments ls et ajustez le décalage dans $ {__ ln [3]}.

Remarque: suivra les liens symboliques.

Orwellophile
la source
1
Ou mettez-le dans un script shell: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano
1
@Luciano Je pense que vous avez totalement manqué le point de ne pas bifurquer et de faire une tâche dans bash plutôt que d'utiliser bash pour enchaîner de nombreuses commandes Unix ensemble de manière inefficace.
Orwellophile
8

Les BSD ont statdes options différentes de celles de GNU coreutils, mais des capacités similaires.

stat -f %z <file name> 

Cela fonctionne sur macOS (testé sur 10.12), FreeBSD , NetBSD et OpenBSD .

user7504315
la source
statCependant, Solaris n'a pas du tout d' utilité.
Palec
6

Lors du traitement de la ls -nsortie, comme alternative aux tableaux de shell mal portables, vous pouvez utiliser les arguments de position, qui forment le seul tableau et sont les seules variables locales du shell standard. Enveloppez l'écrasement des arguments de position dans une fonction pour conserver les arguments d'origine de votre script ou fonction.

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

Cela divise la sortie de en ln -dnfonction IFSdes paramètres actuels de la variable d'environnement, l'assigne aux arguments de position et fait écho au cinquième. Le -dgarantit que les répertoires sont gérés correctement et -ngarantit que les noms d'utilisateurs et de groupes n'ont pas besoin d'être résolus, contrairement à -l. En outre, les noms d'utilisateurs et de groupes contenant des espaces peuvent théoriquement casser la structure de ligne attendue; ils sont généralement interdits, mais cette possibilité incite toujours le programmeur à réfléchir.

Richard
la source
5

Si vous utilisez finddes fileutils GNU:

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

Malheureusement, d'autres implémentations de findne prennent généralement pas en charge -maxdepth, ni -printf. C'est le cas, par exemple, de Solaris et de macOS find.

Suspendu jusqu'à nouvel ordre.
la source
FYI maxdepth n'est pas nécessaire. Il pourrait être réécrit comme size=$(test -f filename && find filename -printf '%s').
Palec
@Palec: le -maxdepthest destiné à éviter findd'être récursif (puisque ce statque l'OP doit remplacer ne l'est pas). Il findmanque un a à votre commande -nameet la testcommande n'est pas nécessaire.
Suspendu jusqu'à nouvel ordre.
@DennisWilliamson findrecherche ses paramètres de manière récursive pour les fichiers correspondant à des critères donnés. Si les paramètres ne sont pas des répertoires, la récursivité est… assez simple. Par conséquent, je teste d'abord qu'il filenames'agit vraiment d'un fichier ordinaire existant, puis j'imprime sa taille en utilisant findqui n'a nulle part où récurer.
Palec
1
find . -maxdepth 1 -type f -name filename -printf '%s'ne fonctionne que si le fichier est dans le répertoire en cours, et il peut toujours examiner chaque fichier dans le répertoire, ce qui peut être lent. Meilleure utilisation (encore plus courte!) find filename -maxdepth 1 -type f -printf '%s'.
Palec
3

Vous pouvez utiliser la findcommande pour obtenir un ensemble de fichiers (ici les fichiers temporaires sont extraits). Ensuite, vous pouvez utiliser la ducommande pour obtenir la taille du fichier de chaque fichier sous une forme lisible par l'homme à l'aide de -hcommutateur.

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

PRODUCTION:

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~
Abhishek Singh
la source
2

Votre premier exemple de Perl ne me paraît pas déraisonnable.

C'est pour des raisons comme celle-ci que j'ai migré de l'écriture de scripts shell (dans bash / sh etc.) à l'écriture de tous les scripts sauf les plus triviaux en Perl. J'ai trouvé que je devais lancer Perl pour des besoins particuliers, et comme je le faisais de plus en plus, je me suis rendu compte qu'écrire les scripts en Perl était probablement plus puissant (en termes de langage et du large éventail de bibliothèques disponibles via CPAN ) et un moyen plus efficace d'atteindre ce que je voulais.

Notez que d'autres langages de script shell (par exemple python / ruby) auront sans aucun doute des fonctionnalités similaires, et vous voudrez peut-être les évaluer pour vos besoins. Je ne parle que de Perl car c'est le langage que j'utilise et que je connais bien.

Brian Agnew
la source
Eh bien, je fais beaucoup d'écriture en Perl moi-même, mais parfois l'outil est choisi pour moi, pas par moi :)
-3

si vous avez Perl sur votre Solaris, utilisez-le. Sinon, ls avec awk est votre prochain meilleur pari, puisque vous n'avez pas de statistiques ou que votre trouvaille n'est pas une trouvaille GNU.

ghostdog74
la source
-3

Il y a une astuce dans Solaris que j'ai utilisée, si vous demandez la taille de plus d'un fichier, il ne renvoie que la taille totale sans nom - incluez donc un fichier vide comme / dev / null comme deuxième fichier:

par exemple, fichier de commande que vous voulez / dev / null

Je ne peux pas me rappeler quelle commande de taille cela fonctionne pour ls / wc / etc - malheureusement, je n'ai pas de boîtier solaris pour le tester.

Martin Beckett
la source
-4

sur Linux, vous pouvez utiliser du -h $FILE, cela fonctionne-t-il aussi sur Solaris?

tricot
la source
1
En fait, les unités peuvent être converties, mais cela montre l'utilisation du disque au lieu de la taille des données du fichier ("taille apparente").
Palec le
-7

Avez-vous essayé du -ks | awk '{print $ 1 * 1024}'. Cela pourrait bien fonctionner.

Aditya
la source
1
Cela montre l'utilisation du disque au lieu de la taille des données du fichier ("taille apparente").
Palec