Las respuestas aceptadas / altamente votadas son geniales, pero carecen de algunos detalles esenciales. Esta publicación cubre los casos sobre cómo manejar mejor cuando falla la expansión del nombre de ruta del shell (glob), cuando los nombres de archivo contienen líneas nuevas incrustadas / símbolos de guión y mueven la redirección de salida del comando fuera del ciclo for al escribir los resultados en un expediente.
Cuando se ejecuta la expansión de glob shell, *
existe la posibilidad de que la expansión falle si no hay archivos presentes en el directorio y se pasará una cadena glob no expandida al comando que se ejecutará, lo que podría tener resultados no deseados. El bash
shell proporciona una opción de shell extendida para este uso nullglob
. Entonces, el bucle se convierte básicamente de la siguiente manera dentro del directorio que contiene sus archivos
shopt -s nullglob
for file in ./*; do
cmdToRun [option] -- "$file"
done
Esto le permite salir de forma segura del ciclo for cuando la expresión ./*
no devuelve ningún archivo (si el directorio está vacío)
o de una manera compatible con POSIX ( nullglob
es bash
específico)
for file in ./*; do
[ -f "$file" ] || continue
cmdToRun [option] -- "$file"
done
Esto le permite ir dentro del ciclo cuando la expresión falla por una vez y la condición [ -f "$file" ]
verifica si la cadena no expandida ./*
es un nombre de archivo válido en ese directorio, lo que no sería. Entonces, en caso de falla de esta condición, el uso continue
vuelve al for
bucle que no se ejecutará posteriormente.
También tenga en cuenta el uso de --
justo antes de pasar el argumento del nombre de archivo. Esto es necesario porque, como se señaló anteriormente, los nombres de archivo de shell pueden contener guiones en cualquier parte del nombre de archivo. Algunos de los comandos de shell interpretan eso y los tratan como una opción de comando cuando el nombre no es cita correctamente y ejecuta el comando pensando si se proporciona el indicador.
Las --
señales de la gama de opciones de línea de comandos en ese caso, que significa que el comando no deben analizar todas las cadenas más allá de este punto como opciones de órdenes, pero sólo como nombres de archivo.
La doble cita de los nombres de archivo resuelve correctamente los casos en que los nombres contienen caracteres globales o espacios en blanco. Pero los nombres de archivo * nix también pueden contener nuevas líneas en ellos. Por lo tanto, limitamos los nombres de archivo con el único carácter que no puede ser parte de un nombre de archivo válido: el byte nulo ( \0
). Dado que bash
internamente utiliza C
cadenas de estilo en las que se utilizan los bytes nulos para indicar el final de la cadena, es el candidato adecuado para esto.
Entonces, usando la printf
opción de shell para delimitar archivos con este byte NULL usando la -d
opción de read
comando, podemos hacer a continuación
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done
Los nullglob
y printf
se envuelven, lo (..)
que significa que básicamente se ejecutan en un subconjunto (shell secundario), porque para evitar la nullglob
opción de reflejarse en el shell principal, una vez que el comando sale. La -d ''
opción de read
comando no es compatible con POSIX, por lo que necesita un bash
shell para que esto se haga. Usando el find
comando esto se puede hacer como
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0)
Para find
implementaciones que no son compatibles -print0
(aparte de las implementaciones de GNU y FreeBSD), esto se puede emular usandoprintf
find . -maxdepth 1 -type f -exec printf '%s\0' {} \; | xargs -0 cmdToRun [option] --
Otra solución importante es mover la redirección fuera del ciclo for para reducir una gran cantidad de E / S de archivo. Cuando se usa dentro del bucle, el shell debe ejecutar llamadas al sistema dos veces para cada iteración del bucle for, una para abrir y otra para cerrar el descriptor de archivo asociado con el archivo. Esto se convertirá en un cuello de botella en su rendimiento para ejecutar iteraciones grandes. La sugerencia recomendada sería moverlo fuera del ciclo.
Extendiendo el código anterior con estas correcciones, podría hacer
( shopt -s nullglob; printf '%s\0' ./* ) | while read -rd '' file; do
cmdToRun [option] -- "$file"
done > results.out
que básicamente pondrá el contenido de su comando para cada iteración de su entrada de archivo en stdout y cuando finalice el bucle, abra el archivo de destino una vez para escribir el contenido de stdout y guardarlo. La find
versión equivalente de la misma sería
while IFS= read -r -d '' file; do
cmdToRun [option] -- "$file"
done < <(find -maxdepth 1 -type f -print0) > results.out
ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out