¿Forma portátil de obtener el tamaño del archivo (en bytes) en el shell?

121

En Linux, lo uso stat --format="%s" FILE, pero Solaris al que tengo acceso no tiene comando stat. ¿Qué debo usar entonces?

Estoy escribiendo scripts Bash y realmente no puedo instalar ningún software nuevo en el sistema.

Ya he considerado usar:

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

o incluso:

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

Pero ninguno de estos parece sensato: ¿ejecutar Perl solo para obtener el tamaño del archivo? ¿O ejecutar 2 comandos para hacer lo mismo?


fuente
1
bueno, un script bash es software, y si puede ponerlo en el sistema, puede instalar software.
solo alguien
4
Técnicamente, cierto. Quise decir que no tengo privilegios de root y no puedo instalar nuevos paquetes. Seguro que la instalación en el directorio de inicio es posible. Pero no realmente cuando tengo que hacer que el script sea portátil y la instalación en máquinas "X", los nuevos paquetes adicionales se vuelven complicados.

Respuestas:

207

wc -c < filename(abreviatura de recuento de palabras, -cimprime el recuento de bytes) es una solución POSIX portátil . Solo el formato de salida puede no ser uniforme en todas las plataformas, ya que algunos espacios pueden estar precedidos (como es el caso de Solaris).

No omita la redirección de entrada. Cuando el archivo se pasa como argumento, el nombre del archivo se imprime después del recuento de bytes.

Me preocupaba que no funcionara con archivos binarios, pero funciona bien tanto en Linux como en Solaris. Puedes probarlo con wc -c < /usr/bin/wc. Además, las utilidades POSIX están garantizadas para manejar archivos binarios , a menos que se especifique lo contrario explícitamente.

Carl Smotricz
fuente
67
O simplemente wc -c < filesi no desea que aparezca el nombre del archivo.
caf
34
Sin embargo, si no me equivoco, wcen una canalización debe read()todo el flujo contar los bytes. Las soluciones ls/ awk(y similares) usan una llamada al sistema para obtener el tamaño, que debería ser tiempo lineal (versus O (tamaño))
jmtd
1
Recuerdo wchaber sido muy lento la última vez que hice eso en un disco duro lleno. Fue lo suficientemente lento como para poder volver a escribir el guión antes de que terminara el primero, vine aquí para recordar cómo lo hice jajaja.
Camilo Martin
6
Yo no usaría wc -c; se ve mucho más ordenado pero ls+ awkes mejor para la velocidad / uso de recursos. Además, solo quería señalar que en realidad también necesita postprocesar los resultados wcporque en algunos sistemas tendrá espacios en blanco antes del resultado, que es posible que deba eliminar antes de poder hacer comparaciones.
Haravikk
3
wc -ces genial, pero no funcionará si no tiene acceso de lectura al archivo.
Silas
41

Terminé escribiendo mi propio programa (realmente pequeño) para mostrar solo el tamaño. Más información aquí: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

En mi opinión, las dos formas más limpias con las herramientas comunes de Linux son:

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

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

Pero simplemente no quiero escribir parámetros o canalizar la salida solo para obtener un tamaño de archivo, así que estoy usando mi propio bfsize.

fwhacking
fuente
2
La primera línea de la descripción del problema indica que stat no es una opción, y wc -c es la respuesta principal durante más de un año, así que no estoy seguro de cuál es el punto de esta respuesta.
22
El punto está en personas como yo que encuentran esta pregunta SO en Google y stat es una opción para ellos.
el
3
Estoy trabajando en un sistema integrado donde wc -ctoma 4090 mseg en un archivo de 10 MB en lugar de "0" mseg stat -c %s, así que estoy de acuerdo en que es útil tener soluciones alternativas incluso cuando no responden la pregunta exacta planteada.
Robert Calhoun
3
"stat -c" no es portátil / no acepta los mismos argumentos en MacOS que en Linux. "wc -c" será muy lento para archivos grandes.
Orwellophile
2
stat tampoco es portátil. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]
27

Aunque dunormalmente imprime el uso del disco y no el tamaño real de los datos, las coreutils de GNU dupueden imprimir el "tamaño aparente" del archivo en bytes:

du -b FILE

Pero no funcionará con BSD, Solaris, macOS, ...

fwhacking
fuente
3
En MacOS X, brew install coreutilsy gdu -blogrará el mismo efecto
Jose Alban
1
Prefiero este método porque wcnecesita leer todo el archivo antes de dar un resultado, dues inmediato.
CousinCocaine
2
POSIX menciona du -ben un contexto completamente diferente en la durazón de ser .
Palec
Esto usa solo la lstatllamada, por lo que su rendimiento no depende del tamaño del archivo. Más corto que stat -c '%s', pero menos intuitivo y funciona de manera diferente para las carpetas (imprime el tamaño de cada archivo dentro).
Palec
FreeBSDdu puede acercarse usando du -A -B1, pero aún imprime el resultado en múltiplos de 1024B bloques. No logré que imprimiera el recuento de bytes. Incluso la configuración BLOCKSIZE=1en el entorno no ayuda, porque entonces se utilizan bloques 512B.
Palec
13

Finalmente decidí usar ls y la expansión de matriz bash:

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

no es realmente agradable, pero al menos solo hace 1 fork + execve, y no depende del lenguaje de programación secundario (perl / ruby ​​/ python / lo que sea)


fuente
Solo un aparte: la 'l' en '-ln' no es necesaria; '-n' es exactamente lo mismo que '-ln'
prohibido
No, no es. Simplemente compare los resultados.
1
Uno podría suponer que el portátil ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }no necesita bifurcaciones para el segundo paso de la canalización, ya que solo usa incorporados, sino que Bash 4.2.37 en Linux bifurca dos veces (aunque solo una execve).
Palec
read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"funciona con un solo fork y single exec, pero usa un archivo temporal para here-string. Puede hacerse portátil reemplazando here-string por here-document compatible con POSX . Por cierto, tenga execen cuenta el en la subcapa. Sin eso, Bash realiza una bifurcación para el subshell y otra para el comando que se ejecuta dentro. Este es el caso del código que proporciona en esta respuesta. también.
Palec
1
El -les superfluo en presencia de -n. Citando POSIX lspágina de manual : -n: Active la -lopción (ELL), pero al escribir el propietario del archivo o grupo, escriba UID numérico del archivo o GID en lugar del nombre de usuario o grupo, respectivamente. Desactivar los -C, -my -xopciones.
Palec
8

La solución más rápida multiplataforma (solo usa un solo fork () para ls , no intenta contar los caracteres reales, no genera awk, perl, etc. innecesarios).

Probado en MacOS, Linux; puede requerir una pequeña modificación para Solaris:

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

Si es necesario, simplifique los argumentos de ls y ajuste el desplazamiento en $ {__ ln [3]}.

Nota: seguirá los enlaces simbólicos.

Orwellophile
fuente
1
O póngalo en un script de shell: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano
1
@Luciano Creo que has perdido totalmente el punto de no bifurcar y hacer una tarea en bash en lugar de usar bash para encadenar muchos comandos de Unix de manera ineficiente.
Orwellophile
8

Los BSD tienen statopciones diferentes a las de GNU coreutils, pero capacidades similares.

stat -f %z <file name> 

Esto funciona en macOS (probado en 10.12), FreeBSD , NetBSD y OpenBSD .

usuario7504315
fuente
Sin embargo, Solaris no tiene ninguna statutilidad.
Palec
6

Al procesar la ls -nsalida, como alternativa a las matrices de shell poco portátiles, puede usar los argumentos posicionales, que forman la única matriz y son las únicas variables locales en la shell estándar. Envuelva la sobrescritura de los argumentos posicionales en una función para preservar los argumentos originales de su script o función.

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

Esto divide la salida de ln -dnsegún IFSla configuración actual de la variable de entorno, la asigna a los argumentos posicionales y se hace eco del quinto. El -dasegura que los directorios se manejen correctamente y -nasegura que los nombres de usuarios y grupos no necesitan ser resueltos, a diferencia de con -l. Además, los nombres de usuarios y grupos que contienen espacios en blanco podrían, en teoría, romper la estructura de línea esperada; por lo general, no se permiten, pero esta posibilidad aún hace que el programador se detenga y piense.

Ricardo
fuente
5

Si usa finddesde GNU fileutils:

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

Desafortunadamente, otras implementaciones de findnormalmente no admiten -maxdepthni -printf. Este es el caso de, por ejemplo, Solaris y macOS find.

Pausado hasta nuevo aviso.
fuente
FYI maxdepth no es necesario. Podría reescribirse como size=$(test -f filename && find filename -printf '%s').
Palec
@Palec: El -maxdepthobjetivo es evitar que findsea ​​recursivo (ya statque lo que el OP necesita reemplazar no lo es). A su findcomando le falta un -namey el testcomando no es necesario.
Pausado hasta nuevo aviso.
@DennisWilliamson findbusca en sus parámetros de forma recursiva archivos que coincidan con los criterios dados. Si los parámetros no son directorios, la recursividad es ... bastante simple. Por lo tanto, primero pruebo que filenamees realmente un archivo ordinario existente, y luego imprimo su tamaño usando findque no tiene ningún lugar para recurrir.
Palec
1
find . -maxdepth 1 -type f -name filename -printf '%s'sólo funciona si el archivo está en el directorio actual y aún puede examinar cada archivo en el directorio, lo que puede ser lento. Mejor uso (¡incluso más corto!) find filename -maxdepth 1 -type f -printf '%s'.
Palec
3

Puede usar el findcomando para obtener un conjunto de archivos (aquí se extraen los archivos temporales). Luego, puede usar el ducomando para obtener el tamaño de archivo de cada archivo en forma legible por humanos usando el -hinterruptor.

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

SALIDA:

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
fuente
2

Tu primer ejemplo de Perl no me parece irrazonable.

Es por razones como esta que migré de escribir scripts de shell (en bash / sh, etc.) a escribir todos los scripts menos los más triviales en Perl. Descubrí que tenía que lanzar Perl para requisitos particulares y, a medida que lo hacía cada vez más, me di cuenta de que escribir los scripts en Perl era probablemente una forma más poderosa (en términos del lenguaje y la amplia gama de bibliotecas disponibles a través de CPAN ) y una forma más eficiente de lograr lo que quería.

Tenga en cuenta que otros lenguajes de secuencias de comandos de shell (por ejemplo, python / ruby) sin duda tendrán instalaciones similares, y es posible que desee evaluarlas para sus propósitos. Solo hablo de Perl porque ese es el lenguaje que uso y con el que estoy familiarizado.

Brian Agnew
fuente
Bueno, yo mismo escribo mucho en Perl, pero a veces la herramienta es elegida por mí, no por mí :)
-3

si tiene Perl en su Solaris, utilícelo. De lo contrario, ls con awk es su siguiente mejor opción, ya que no tiene stat o su búsqueda no es GNU find.

ghostdog74
fuente
-3

Hay un truco en Solaris que he usado, si solicita el tamaño de más de un archivo, devuelve solo el tamaño total sin nombres, así que incluya un archivo vacío como / dev / null como segundo archivo:

por ejemplo, archivo de comando que desea / dev / null

No puedo recordar qué comando de tamaño funciona para ls / wc / etc; desafortunadamente no tengo una caja solaris para probarlo.

Martin Beckett
fuente
-4

en Linux que puede usar du -h $FILE, ¿eso también funciona en Solaris?

Knittl
fuente
1
En realidad, las unidades se pueden convertir, pero esto muestra el uso del disco en lugar del tamaño de los datos del archivo ("tamaño aparente").
Palec
-7

¿Probaste du -ks | awk '{imprimir $ 1 * 1024}'. Eso podría funcionar.

Aditya
fuente
1
Esto muestra el uso del disco en lugar del tamaño de los datos del archivo ("tamaño aparente").
Palec