¿Cómo cambiar el nombre de todas las carpetas y archivos a minúsculas en Linux?

StackOverflow https://stackoverflow.com/questions/152514

  •  02-07-2019
  •  | 
  •  

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.

  1. 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.

  2. 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).

  3. 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.

¿Fue útil?

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:

  1. ¿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 y aBcDeF ? ¿Debería el script de cambio de nombre abortar o simplemente advertir y continuar?

  2. ¿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?

  3. 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

Cómo convertir una cadena a minúsculas Bash?

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

¿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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top