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áfooy se repetirábarincluso 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/tmppara, por ejemplo, buscar directorios para eliminar, podría terminar eliminándolo/etc/passwd.Respuestas:
fuente
findparámetros si desea un comportamiento recursivo o no recursivo.find . -mindepth 1 -type dmkdir 'directory name with spaces'en cuatro palabras separadas.-mindepth 1 -maxdepth 1o 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
Dtendrá una barra inclinada final:fuente
ifo[con:for D in */; dofor D in *; do [ -d "${D}" ] && my_command; doneo una combinación de los dos últimos:for D in */; do [ -d $D ] && my_command; donefor D in .* *; doen 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 dotglobpara 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
findcomando.En GNU
find, puede usar el-execdirparámetro:o usando el
-execparámetro:o con
xargscomando: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_commandfuente
my_command.Esto creará una subshell (lo que significa que los valores variables se perderán cuando
whilesalga el bucle):Esto no:
Cualquiera de los dos funcionará si hay espacios en los nombres de directorio.
fuente
find ... -print0ywhile 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 defindestá dividida en cadenas, por lo que tendrá las partes separadas del nombre como valores separados$f, lo que hace que cumpla o no cite$fel argumento de expansión.findLa salida está orientada a la línea (un nombre a una línea, en teoría, pero ver más abajo) con la-printacción predeterminada .find -printno 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/passwduna línea separada en su nombre. Manejar nombres de archivos/uploado/tmpdirectorios 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-execopción con,+;p. Ej.find .... -exec mycommand +;#this is same as passing to xargso use un bucle while
fuente
find -execy pasar axargs:findignorará el valor de salida del comando que se está ejecutando, mientrasxargsque fallará en una salida distinta de cero. Cualquiera de los dos puede ser correcto, según sus necesidades.find ... -print0 | while IFS= read -r des más seguro: admite nombres que comienzan o terminan en espacios en blanco y nombres que contienen literales de nueva línea.