¿Cómo cambiar el nombre de todas las carpetas y archivos a minúsculas en Linux?
Pregunta
Tengo que cambiar el nombre de un árbol de carpetas completo de forma recursiva para que no aparezca ninguna letra mayúscula en cualquier lugar (es un código fuente de C ++, pero eso no debería importar). Puntos de bonificación por ignorar los archivos / carpetas de control de CVS y SVN. La forma preferida sería un script de shell, ya que el shell debería estar disponible en cualquier caja de Linux.
Hubo algunos argumentos válidos sobre los detalles del cambio de nombre del archivo.
-
Creo que los archivos con los mismos nombres en minúsculas deberían sobrescribirse, es un problema del usuario. Cuando se comprueba en un sistema de archivos que ignora los casos, también se sobrescribirá el primero con este último.
-
Consideraría los caracteres A-Z y los transformaría en a-z, todo lo demás solo requiere problemas (al menos con el código fuente).
-
El script sería necesario para ejecutar una compilación en un sistema Linux, por lo que creo que los cambios en CVS o SVN deben omitirse. Después de todo, es solo una compra de rasguño. Tal vez una " exportación " es más apropiado.
Solución
Una versión concisa que utiliza el comando " rename "
.
find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
Esto evita problemas con el cambio de nombre de los directorios antes de los archivos y el intento de mover los archivos a directorios no existentes (p. ej. " A / A "
a " a / a "
).
O, una versión más detallada sin usar " renombrar "
.
for SRC in `find my_root_dir -depth`
do
DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
if [ "${SRC}" != "${DST}" ]
then
[ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
fi
done
P. S.
Este último permite más flexibilidad con el comando de movimiento (por ejemplo, " svn mv "
).
Otros consejos
más pequeño todavía me gusta bastante
rename 'y/A-Z/a-z/' *
En los sistemas de archivos que no distinguen entre mayúsculas y minúsculas, como HFS + de OS X, deseará agregar la marca -f
rename -f 'y/A-Z/a-z/' *
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Simplemente intente seguir si no necesita preocuparse por la eficiencia.
zip -r foo.zip foo/*
unzip -LL foo.zip
La mayoría de las respuestas anteriores son peligrosas porque no tratan con nombres que contienen caracteres impares. Su apuesta más segura para este tipo de cosas es usar la opción -print0 de find, que terminará los nombres de archivos con ascii NUL en lugar de \ n. Aquí presento este script, que solo altera los archivos y no los nombres de directorio para no confundir la búsqueda.
find . -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash
")/$(basename "<*>");
d=$(dirname "<*>")/$(basename "<*>"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'
Lo probé y funciona con nombres de archivos que contienen espacios, todo tipo de citas, etc. Esto es importante porque si ejecuta, como root, uno de esos otros scripts en un árbol que incluye el archivo creado por:
<*>... bueno, adivina qué ...
Esto funciona si ya tienes o configuraste el comando renombrar (por ejemplo, a través de brew install en Mac):
rename --lower-case --force somedir/*
Hombre, a ustedes les gusta complicar más las cosas ...
renombrar 'y / A-Z / a-z /' *
Esto funciona en CentOS / Redhat u otras distribuciones sin el script de Perl rename
:
for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done
Fuente: https://linuxconfig.org/rename- todos los archivos de mayúsculas a minúsculas en caracteres
(en algunas distribuciones, el comando predeterminado rename
proviene de util-linux, y esa es una herramienta diferente e incompatible)
Usando el reparador de nombre de archivo de Larry Wall
$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
$was = Usando el reparador de nombre de archivo de Larry Wall
find | fix 'tr/A-Z/a-z/'
es tan simple como
<*>
(donde la corrección es, por supuesto, el script anterior)
;
eval $op;
die $@ if $@;
rename($was, Usando el reparador de nombre de archivo de Larry Wall
<*>
es tan simple como
<*>
(donde la corrección es, por supuesto, el script anterior)
) unless $was eq Usando el reparador de nombre de archivo de Larry Wall
<*>
es tan simple como
<*>
(donde la corrección es, por supuesto, el script anterior)
;
}
es tan simple como
<*>(donde la corrección es, por supuesto, el script anterior)
La pregunta original solicitada para ignorar los directorios SVN y CVS, que se puede hacer agregando -prune al comando de búsqueda. Por ejemplo, para ignorar CVS:
find . -name CVS -prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print
[editar] Intenté esto, e incrustar la traducción en minúscula dentro del hallazgo no funcionó por razones que realmente no entiendo. Por lo tanto, modifíquelo para:
<*>gt; cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
<*>gt; chmod u+x tolower
<*>gt; find . -name CVS -prune -o -exec tolower '{}' \;
Ian
Aquí está mi solución subóptima, usando un script bash Shell:
#!/bin/bash
# first, rename all folders
for f in `find . -depth ! -name CVS -type d`; do
g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
if [ "xxx$f" != "xxx$g" ]; then
echo "Renaming folder $f"
mv -f "$f" "$g"
fi
done
# now, rename all files
for f in `find . ! -type d`; do
g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
if [ "xxx$f" != "xxx$g" ]; then
echo "Renaming file $f"
mv -f "$f" "$g"
fi
done
Editar: Hice algunas modificaciones basadas en las sugerencias hasta ahora. Ahora todas las carpetas se renombran correctamente, mv no hace preguntas cuando los permisos no coinciden, y las carpetas CVS no se renombran (los archivos de control CVS dentro de esa carpeta aún se renombran, desafortunadamente).
Editar: desde " encuentra -depth " y " buscar | ordenar -r " ambos devuelven la lista de carpetas en un orden utilizable para cambiar el nombre, prefiero usar " -depth " para buscar carpetas.
Este es un script de shell pequeño que hace lo que usted solicitó:
root_directory="${1?-please specify parent directory}"
do_it () {
awk '{ lc= tolower(<*>); if (lc != <*>) print "mv \"" <*> "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it
Observe la acción -depth en la primera búsqueda.
La publicación anterior funcionará perfectamente fuera de la caja o con algunos ajustes para casos simples, pero hay algunas situaciones que tal vez quiera tener en cuenta antes de ejecutar el cambio de nombre del lote:
-
¿Qué debería suceder si tiene dos o más nombres en el mismo nivel en la jerarquía de rutas que difieren solo por caso, como
ABCdef
,abcDEF
yaBcDeF
? ¿Debería el script de cambio de nombre abortar o simplemente advertir y continuar? -
¿Cómo define la minúscula para los nombres que no pertenecen a US-ASCII? Si dichos nombres pueden estar presentes, ¿se debe realizar primero una verificación y exclusión?
-
Si está ejecutando una operación de cambio de nombre en las copias de trabajo de CVS o SVN, podría corromper la copia de trabajo si cambia el caso en los nombres de archivos o directorios. ¿El script también debería encontrar y ajustar los archivos administrativos internos, como .svn / entries o CVS / Entries?
for f in `find -depth`; do mv ${f} ${f,,} ; done
find -depth
imprime cada archivo y directorio, con el contenido de un directorio impreso antes del directorio en sí. $ {f ,,}
minúscula el nombre del archivo.
Con MacOS,
Instale el paquete rename
,
brew install rename
Uso,
find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
Este comando busca todos los archivos con la extensión * .py
y convierte los nombres de archivo a minúsculas.
`f` - forces a rename
Por ejemplo,
$ find . -iname "*.py" -type f
./sample/Sample_File.py
./sample_file.py
$ find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
$ find . -iname "*.py" -type f
./sample/sample_file.py
./sample_file.py
No portátil, solo Zsh, pero bastante conciso.
Primero, asegúrate de que zmv
esté cargado.
autoload -U zmv
También, asegúrese de que extendedglob
esté activado:
setopt extendedglob
Luego usa:
zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'
Para archivos y directorios en minúsculas recursivas donde el nombre no es CVS .
En OSX, mv -f muestra " mismo archivo " error, así que renombro dos veces.
for i in `find . -name "*" -type f |grep -e "[A-Z]"`; do j=`echo $i | tr '[A-Z]' '[a-z]' | sed s/\-1$//`; mv $i $i-1; mv $i-1 $j; done
Necesitaba hacer esto en una configuración de cygwin en Windows 7 y encontré que tenía errores de sintaxis con las sugerencias de arriba que probé (aunque es posible que haya perdido una opción de trabajo), sin embargo, esta solución viene directamente de foros de ubutu resueltos de la lata :-)
ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done
(nb había reemplazado previamente los espacios en blanco con guiones bajos usando
for f in *\ *; do mv "$f" "${f// /_}"; done
Me gustaría alcanzar Python en esta situación, para evitar asumir de forma optimista las rutas sin espacios ni barras. También encontré que python2
tiende a instalarse en más lugares que rename
.
#!/usr/bin/env python2
import sys, os
def rename_dir(directory):
print('DEBUG: rename('+directory+')')
# rename current directory if needed
os.rename(directory, directory.lower())
directory = directory.lower()
# rename children
for fn in os.listdir(directory):
path = os.path.join(directory, fn)
os.rename(path, path.lower())
path = path.lower()
# rename children within, if this child is a directory
if os.path.isdir(path):
rename_dir(path)
# run program, using the first argument passed to this python script as the name of the folder
rename_dir(sys.argv[1])
Largo pero " Funciona sin sorpresas & amp; No hay instalaciones "
Este script maneja los nombres de archivo con espacios, comillas, otros caracteres inusuales y Unicode, funciona en sistemas de archivos que no distinguen entre mayúsculas y minúsculas y en la mayoría de los entornos Unix-y que tienen instalados bash y awk ( es decir, casi todos). También informa de colisiones si las hay (dejando el nombre del archivo en mayúsculas) y, por supuesto, cambia el nombre de ambos archivos & amp; Directorios y trabajos recursivos. Finalmente, es altamente adaptable: puede ajustar el comando de búsqueda para apuntar a los archivos / directorios que desee y puede ajustar awk para hacer otras manipulaciones de nombre. Tenga en cuenta que al " maneja Unicode " Quiero decir que efectivamente convertirá su caso (no los ignore como respuestas que usan tr
).
# adapt the following command _IF_ you want to deal with specific files/dirs
find . -depth -mindepth 1 -exec bash -c '
for file do
# adapt the awk command if you wish to rename to something other than lowercase
newname=$(dirname "$file")/$(basename "$file" | awk "{print tolower(\<*>)}")
if [ "$file" != "$newname" ] ; then
# the extra step with the temp filename is for case-insensitive filesystems
if [ ! -e "$newname" ] && [ ! -e "$newname.lcrnm.tmp" ] ; then
mv -T "$file" "$newname.lcrnm.tmp" && mv -T "$newname.lcrnm.tmp" "$newname"
else
echo "ERROR: Name already exists: $newname"
fi
fi
done
' sh {} +
Referencias
Mi script se basa en estas excelentes respuestas:
https://unix.stackexchange.com/questions / 9496 / looping-through-files-with-spaces-in-the-names
El enfoque más simple que encontré en MacOSX fue usar el paquete rename de http://plasmasturm.org/code / rename / :
brew install rename
rename --force --lower-case --nows *
: forzar Renombrar incluso cuando ya existe un archivo con el nombre de destino.
--lower-case Convierte nombres de archivos a minúsculas.
--nows Reemplaza todas las secuencias de espacios en blanco en el nombre de archivo con caracteres de subrayado únicos.
( find YOURDIR -type d | sort -r;
find yourdir -type f ) |
grep -v /CVS | grep -v /SVN |
while read f; do mv -v $f `echo $f | tr '[A-Z]' '[a-z]'`; done
Primero renombra los directorios de abajo hacia arriba sort -r (donde -depth no está disponible), luego los archivos. Luego, grep -v / CVS en lugar de find ...- prune porque es más sencillo. Para directorios grandes, for f in ... puede desbordar algunos buffers de shell. Utilice encontrar ... | mientras lees para evitar eso.
Y sí, esto eliminará los archivos que difieren solo en el caso ...
Slugify Rename (regex)
No es exactamente lo que pidió el OP, sino lo que esperaba encontrar en esta página:
Un " slugify " versión para renombrar archivos por lo que son similares a las URL
(es decir, solo incluye alfanuméricos, puntos y guiones):
rename "s/[^a-zA-Z0-9\.]+/-/g" filename
Si usa Arch Linux , puede instalar rename
del paquete AUR
que proporciona el comando renamexm
como / usr / bin / renamexm
ejecutable y su página manual junto con ella.
Es una herramienta realmente poderosa para cambiar rápidamente el nombre de archivos y directorios.
Convertir a minúsculas
rename -l Developers.mp3 # or --lowcase
Convertir a mayúsculas
rename -u developers.mp3 # or --upcase, long option
Otras opciones
-R --recursive # directory and its children
-t --test # dry run, output but don't rename
-o --owner # change file owner as well to user specified
-v --verbose # output what file is renamed and its new name
-s/str/str2 # substite string on pattern
--yes # Confirm all actions
Puede obtener el archivo Developers.mp3 de ejemplo desde aquí , si es necesario;)
¿Nadie sugiere tipografía?
typeset -l new # always lowercase
find $topPoint | # not using xargs to make this more readable
while read old
do mv "$old" "$new" # quotes for those annoying embedded spaces
done
En las emulaciones de Windows como Git Bash, esto puede fallar porque Windows no distingue entre mayúsculas y minúsculas debajo del capó. Para aquellos, agregue un paso que mv sea el archivo a otro nombre primero, como " $ old.tmp " ;, luego a $ new.
Esto también funciona bien en macOS:
ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh
No he probado los scripts más elaborados que se mencionan aquí, pero ninguna de las versiones de la línea de comandos funcionó para mí en mi Synology NAS. rename
no está disponible, y muchas de las variaciones de find
fallan porque parece que se adhieren al nombre anterior de la ruta cuyo nombre ya se ha cambiado (por ejemplo, si encuentra ./FOO
seguido de ./FOO/BAR
, cambiando el nombre de ./FOO
a ./foo
aún continuará en la lista < código> ./ FOO / BAR aunque esa ruta ya no sea válida). El comando anterior funcionó para mí sin ningún problema.
Lo que sigue es una explicación de cada parte del comando:
find . -depth -name '*[A-Z]*'
Esto buscará cualquier archivo del directorio actual (cambie .
a cualquier directorio que quiera procesar), usando una búsqueda en profundidad (por ejemplo, listará ./foo / bar
antes de ./foo
), pero solo para los archivos que contienen un carácter en mayúscula. El filtro -name
solo se aplica al nombre del archivo base, no a la ruta completa. Así que esto listará ./FOO/BAR
pero no ./FOO/bar
. Esto está bien, ya que no queremos cambiar el nombre de ./FOO/bar
. Sin embargo, queremos cambiar el nombre de ./FOO
, pero ese es el que aparece más adelante (es por esto que -depth
es importante).
Este comando en sí mismo es particularmente útil para encontrar los archivos cuyo nombre desea cambiar en primer lugar. Use esto después del comando de cambio de nombre completo para buscar archivos que aún no han sido reemplazados debido a colisiones de nombre de archivo o errores.
sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'
Esta parte lee los archivos generados por find
y los formatea en un comando mv
usando una expresión regular. La opción -n
evita que sed
imprima la entrada, y el comando p
en la expresión regular de búsqueda y reemplazo genera el texto reemplazado.
La expresión regular en sí consta de dos capturas: la parte hasta la última / (que es el directorio del archivo) y el nombre del archivo en sí. El directorio se deja intacto, pero el nombre del archivo se transforma en minúsculas. Entonces, si find
produce ./FOO/BAR
, se convertirá en mv -n -v -T ./FOO/BAR ./FOO/bar. La opción
-n
de mv
asegura que los archivos en minúsculas existentes no se sobrescriban. La opción -v
hace que mv
produzca todos los cambios que realice (o no realice). Si ./FOO/bar
ya existe, produce algo como ./FOO/BAR - > ./FOO/BAR
, notando que no se ha realizado ningún cambio). El -T
es muy importante aquí: trata el archivo de destino como un directorio. Esto asegurará que ./FOO/BAR
no se mueva a ./FOO/bar
si ese directorio existe.
Use esto junto con find
para generar una lista de comandos que se ejecutarán (útil para verificar qué se hará sin hacerlo realmente)
sh
Esto se explica por sí mismo. Dirige todos los comandos mv
generados al intérprete de shell. Puede reemplazarlo con bash
o cualquier shell de su agrado.