Agregue un prefijo de carácter de subrayado a todos los directorios de un directorio

Agregue un prefijo de carácter de subrayado a todos los directorios de un directorio

Tengo varios directorios dentro de un directorio cuyo nombre necesito cambiar para incluir un carácter de guión bajo "_" al principio del nombre.

Como prueba, creé un directorio principal cuyo contenido tiene tres directorios:

dir1, dir2 and dir3; three files: file1, file2, file3

y tres archivos con prefijo de puntos:

.file1, .file2, .file3 

Hasta ahora, he intentado usar gfind, después de haberlo instalado findutilsen Mac:

gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

Esto devuelve:

mv: rename ./. to ./_.: Invalid argument

Entonces, intenté usar cambiar nombre con buscar:

find . -type d -exec rename 's/^/_/' {} \;

Esto devuelve:

Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory

¿Alguien tiene una solución viable o sabe dónde podría estar metiendo la pata?

Respuesta1

Si lo hice bien, tienes archivos y directorios como este:

$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1  .file2  .file3  dir1  dir2  dir3  file1  file2  file3

y desea cambiar el nombre de los directorios a _dir1,, _dir2?_dir3

Eso findparece printf mvfactible. Veamos qué imprime:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"

Ese primer comando generará un error, ya que el "punto" es especial y no puedes cambiarle el nombre. Pero los demás deberían estar bien y deberían cambiar el nombre de los directorios como quisiera:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1  _dir2  _dir3

Pero canalizar comandos a un shell es un poco feo, y si los nombres de los archivos son lo suficientemente extraños, los resultados serán sorprendentes (por ejemplo, si contienen $signos que activarán la sustitución de variables o comandos en el shell).

Si todos los archivos están en un nivel, entonces esto debería funcionar:

for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done

(Aunque si _$xya existe, $xse trasladará a él).

Si desea incluir directorios cuyos nombres comiencen con un punto, utilícelo shopt -s dotglobde antemano.

Esto también está cerca:

find . -type d -exec rename 's/^/_/' {} \;

Pero dado que findlas renamerutas comienzan con ./, debemos tenerlo en cuenta. En un solo nivel, esto debería funcionar (cambia el encabezado ./a ./_):

find . -type d -exec rename 's,^./,./_,' {} \;

Para obtener los directorios en todos los niveles, find -execdirpuede ser más fácil de usar. Ejecuta el comando en el directorio del archivo. Necesitamos -depthmanejar los cambios de nombre en el orden correcto.

find . -depth -type d -execdir rename 's,^./,./_,' {} \;

Quizás agregue ! -name .también. p.ej

$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls  _foo
_dir1  _dir2

Respuesta2

El problema con su primer intento es que está intentando cambiar el nombre .de sí mismo (el directorio actual), además de todos los subdirectorios. Saltarlo.

gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

Pero en realidad, no hagas esto. nunca coloques cosas en sh.esto no funcionaa menos que los nombres de sus archivos no contengan ningún carácter especial del shell. Si tiene un archivo llamado `rm -r ~`(un nombre de archivo perfectamente válido, aunque inusual), entonces pop va a su directorio de inicio. Invoca un shell -execcomo muestro a continuación.

Su segundo intento no funciona porque está insertando el _al principio de la ruta completa, en lugar de agregarlo al principio del nombre base, es decir, después del último /.

find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;

Ninguno de estos funciona realmente si tiene directorios anidados, porque cambian el nombre de los directorios mientras findlos atraviesa. Cuando haces esto, necesitas actuar sobre el contenido de un directorio antes de actuar sobre el directorio mismo, con la -depthopción.

find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;

De manera más portátil, llame a un shell y ejecute mv. Esto funciona en cualquier sistema POSIX.

find . -depth -type d -name . -o -exec sh -c '
    for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +

Si no hay subdirectorios anidados, puede utilizar un bucle de shell simple. Si no desea cambiar el nombre de los directorios cuyo nombre comienza con un punto, simplemente use el */comodín, que solo coincide con directorios y enlaces simbólicos a directorios.

for d in */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi   # robustness in case there are no subdirectories
  if [ -L "$d" ]; then continue; fi     # optional: skip symbolic links to directories
  mv -- "$d" "_${d%/}"
done

Si desea cambiar el nombre de todos los directorios, incluidos aquellos cuyo nombre comienza con un punto, incluya .*también el comodín.

for d in .*/ */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
  if [ -L "$d" ]; then continue; fi
  mv -- "$d" "_${d%/}"
done

Tenga en cuenta que cuando se muda fooa _foo, si ya hay un directorio llamado _foo, esto mueve el antiguo fooa _foo/foo. Para protegerse contra esto, agregue una prueba explícita.

O, alternativamente, utilice zsh. Está preinstalado en macOS. Primero ejecute autoload -U zmv(ponga esto en su .zshrco en su script) para cargar elzmvfunción, entonces la versión no recursiva (incluidos los archivos punto, excluyendo los enlaces simbólicos)  (/D)es un conjunto declasificatorios globales):

zmv -Q '*(/D)' '_$f'

o la versión recursiva (usandomodificadorespara extraer el nombre del directorio y el nombre base):

zmv -Q '**/*(/D)' '$f:h/_$f:t'

zmvSe negará a hacer un movimiento si el objetivo ya existe.

información relacionada