cd en todos los directorios, ejecute el comando en los archivos de ese directorio y regrese al directorio actual anterior

41

Estoy intentando escribir un script que se ejecutará en un directorio dado con muchos subdirectorios de un solo nivel. El script creará un cd en cada uno de los subdirectorios, ejecutará un comando en los archivos del directorio y saldrá para continuar en el siguiente directorio. ¿Cuál es la mejor manera de hacer esto?

Algo Jones
fuente
1
Dado el nivel actual de información dada, no veo relevancia para youtube-dl.
HalosGhost

Respuestas:

83
for d in ./*/ ; do (cd "$d" && somecommand); done
psusi
fuente
12
Entonces, dado que el que responde omitió cualquier tipo de explicación, intentaré una. for d in ./*/inicia un ciclo que almacena cada elemento en ./*/(una lista de archivos / carpetas, en este caso) en una variable $d. do (cd "$d" && somecommand);comienza el cuerpo del bucle. Dentro del cuerpo, inicia una subshell y ejecuta los comandos cdy somecommand. Como es un shell hijo, el shell padre (el shell desde el que está ejecutando este comando) retiene su CWD y otras variables de entorno. donesimplemente cierra el cuerpo del bucle.
Qix
Este método funciona para subdirectorios de directorios: for d in ./*/ ; do (cd "$d" && ls); doneno funcionará. Pero, for d in ./*/ ; do (cd "$d" && for d in ./*/ ; do (cd "$d" && ls); done ); donefuncionará. -utilizando ls como el comando en este ejemplo.
Michael Dimmitt
-bash: cd: ./*/: No such file or directory
S. Tarik Çetin
15

La mejor manera es no usar cden absoluto:

find some/dir -type f -execdir somecommand {} \;

execdires como exec, pero el directorio de trabajo es diferente:

   -execdir command {} [;|+]
          Like   -exec,   but  the  specified  command  is  run  from  the
          subdirectory containing the matched file, which is not  normally
          the  directory  in  which  you  started  find.  This a much more
          secure  method  for  invoking  commands,  as  it   avoids   race
          conditions  during resolution of the paths to the matched files.

No es POSIX.

muru
fuente
¿Funciona esto con alias? Tengo uno para descargar ciertos archivos, pero no lo reconoce cuando escribo find * /. Link -type f -execdir md $ (cat .link) {} \;
Algo Jones
@SomethingJones no, findejecuta esos comandos, por lo que no sería consciente de los alias. ¿Qué es mdy es .linkun directorio?
muru
.link es un archivo de texto que tiene la URL que necesita descargar. md es un alias para jugar con un conjunto de banderas. ¿Hay alguna manera de darle a conocer los alias?
Algo Jones
@SomethingJones Para su caso de uso particular, en bash: find . -type f -iname '*.link' -execdir ${BASH_ALIASES[md]} -i {} \;No tiene que ver catcon wget, que tiene una -ibandera para leer en una URL de un archivo. Además, esto es algo diferente de su pregunta original (ya que parece estar interesado solo en los archivos nombrados .linky no en otros archivos que puedan estar presentes).
muru
¿Sabes cómo hacer eso con zsh? Intenté lo que me diste y recibo el error "Mala sustitución". Además, ¿cómo podría extraer el contenido del archivo .link? Sé que no lo necesito en este caso, pero imagino que lo haré pronto.
Algo Jones
2
cd -P .
for dir in ./*/
do cd -P "$dir" ||continue
   printf %s\\n "$PWD" >&2
   command && cd "$OLDPWD" || 
! break; done || ! cd - >&2

El comando anterior no necesita hacer subcapas: solo realiza un seguimiento de su progreso en el shell actual alternando $OLDPWDy $PWD. Cuando cd -el shell intercambia el valor de estas dos variables, básicamente, ya que cambia los directorios. También imprime el nombre de cada directorio, ya que funciona allí para stderr.

Acabo de echarle un segundo vistazo y decidí que podría hacer un mejor trabajo con el manejo de errores. Saltará un directorio en el que no puede cd, e cdimprimirá un mensaje sobre por qué stderr, y contará breakcon un código de salida distinto de cero si su commandejecución no es exitosa o si la ejecución commandafecta de alguna manera su capacidad de regresar a su directorio original - $OLDPWD. En ese caso, también hace un cd -último, y escribe el nombre del directorio de trabajo actual resultante en stderr.

mikeserv
fuente
1
for D in ./*; do
    if [ -d "$D" ]; then
        cd "$D"
        run_something
        cd ..
    fi
done
Kirill
fuente