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 findutils
en 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 find
parece printf mv
factible. 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 _$x
ya existe, $x
se trasladará a él).
Si desea incluir directorios cuyos nombres comiencen con un punto, utilícelo shopt -s dotglob
de antemano.
Esto también está cerca:
find . -type d -exec rename 's/^/_/' {} \;
Pero dado que find
las rename
rutas 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 -execdir
puede ser más fácil de usar. Ejecuta el comando en el directorio del archivo. Necesitamos -depth
manejar 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 -exec
como 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 find
los atraviesa. Cuando haces esto, necesitas actuar sobre el contenido de un directorio antes de actuar sobre el directorio mismo, con la -depth
opció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 foo
a _foo
, si ya hay un directorio llamado _foo
, esto mueve el antiguo foo
a _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 .zshrc
o en su script) para cargar elzmv
funció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'
zmv
Se negará a hacer un movimiento si el objetivo ya existe.