Estoy trabajando en un script que necesita realizar una acción en cada subdirectorio de una carpeta específica.
¿Cuál es la forma más eficiente de escribir eso?
bash
command
directory-traversal
Mikewilliamson
fuente
fuente
mkdir 'foo * bar'
causaráfoo
y se repetirábar
incluso si no existen, y*
serán reemplazados por una lista de todos los nombres de archivos, incluso los que no son de directorio).mkdir -p '/tmp/ /etc/passwd /'
: si alguien ejecuta un script siguiendo esta práctica/tmp
para, por ejemplo, buscar directorios para eliminar, podría terminar eliminándolo/etc/passwd
.Respuestas:
fuente
find
parámetros si desea un comportamiento recursivo o no recursivo.find . -mindepth 1 -type d
mkdir 'directory name with spaces'
en cuatro palabras separadas.-mindepth 1 -maxdepth 1
o fue demasiado profundo.Una versión que evita crear un subproceso:
O, si su acción es un solo comando, esto es más conciso:
O una versión aún más concisa ( gracias @enzotib ). Tenga en cuenta que en esta versión cada valor de
D
tendrá una barra inclinada final:fuente
if
o[
con:for D in */; do
for D in *; do [ -d "${D}" ] && my_command; done
o una combinación de los dos últimos:for D in */; do [ -d $D ] && my_command; done
for D in .* *; do
en su lugarfor D in *; do
.La forma más simple y no recursiva es:
Al
/
final dice, usar directorios solamente.No hay necesidad de
fuente
shopt -s dotglob
para incluir archivos de puntos / dotdirs al expandir comodines. Ver también: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html/*
en lugar de*/
con/
que representa la ruta que desea utilizar./*
sería para ruta absoluta mientras*/
que incluiría los subdirectorios de la ubicación actual${d%/*}
Usa el
find
comando.En GNU
find
, puede usar el-execdir
parámetro:o usando el
-exec
parámetro:o con
xargs
comando:O usando for loop:
Para la recursividad, intente globbing extendido (
**/
) en su lugar (habilitado por :)shopt -s extglob
.Para obtener más ejemplos, consulte: ¿Cómo ir a cada directorio y ejecutar un comando? en SO
fuente
-exec {} +
está especificado por POSIX,-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +
es otra opción legal e invoca menos shells que-exec sh -c '...' {} \;
.Prácticas frases sencillas
La opción A es correcta para carpetas con espacios intermedios. Además, generalmente más rápido ya que no imprime cada palabra en el nombre de una carpeta como una entidad separada.
fuente
find . -type d -print0 | xargs -0 -n 1 my_command
fuente
my_command
.Esto creará una subshell (lo que significa que los valores variables se perderán cuando
while
salga el bucle):Esto no:
Cualquiera de los dos funcionará si hay espacios en los nombres de directorio.
fuente
find ... -print0
ywhile IFS="" read -r -d $'\000' dir
-d ''
es menos engañoso sobre la sintaxis y las capacidades de bash, ya que-d $'\000'
implica (falsamente) que$'\000'
de alguna manera es diferente de''
, de hecho, uno podría inferir fácilmente (y nuevamente, falsamente) es que bash admite cadenas de estilo Pascal (longitud especificada, capaz de contener literales NUL) en lugar de cadenas C (delimitadas por NUL, incapaz de contener NUL).Tu podrías intentar:
o similar...
Explicación:
find . -maxdepth 1 -mindepth 1 -type d
: Solo encuentre directorios con una profundidad recursiva máxima de 1 (solo los subdirectorios de $ 1) y una profundidad mínima de 1 (excluye la carpeta actual.
)fuente
cd "$f"
. No funciona cuando la salida defind
está dividida en cadenas, por lo que tendrá las partes separadas del nombre como valores separados$f
, lo que hace que cumpla o no cite$f
el argumento de expansión.find
La salida está orientada a la línea (un nombre a una línea, en teoría, pero ver más abajo) con la-print
acción predeterminada .find -print
no es una forma segura de pasar nombres de archivo arbitrarios, ya que uno puede ejecutar algomkdir -p $'foo\n/etc/passwd\nbar'
y obtener un directorio que tiene/etc/passwd
una línea separada en su nombre. Manejar nombres de archivos/upload
o/tmp
directorios sin cuidado es una excelente manera de obtener ataques de escalada de privilegios.la respuesta aceptada se dividirá en espacios en blanco si los nombres de directorio los tienen, y la sintaxis preferida es
$()
para bash / ksh. Use laGNU find
-exec
opción con,+;
p. Ej.find .... -exec mycommand +;
#this is same as passing to xargs
o use un bucle while
fuente
find -exec
y pasar axargs
:find
ignorará el valor de salida del comando que se está ejecutando, mientrasxargs
que fallará en una salida distinta de cero. Cualquiera de los dos puede ser correcto, según sus necesidades.find ... -print0 | while IFS= read -r d
es más seguro: admite nombres que comienzan o terminan en espacios en blanco y nombres que contienen literales de nueva línea.