Versión 1.0 - julio de 2005
Versión 2.0 - julio de 2015 - Mejorados algunos guiones y aņadidos nuevos
Cuando recibimos ficheros puede que tengan nombres con
espacios, permisos inadecuados, mayúsculas, eñes,
acentos, caracteres reservados de Bash, etc. A mi particularmente
me gustan los nombres sin espacios, todo en minúsculas y con
permisos 755 (rwxr-xr-x) para los directorios y 644 (rw-r--r--)
para los ficheros. Los siguientes guiones resuelven el problema
pues funcionan recursivamente. Son el resultado de preguntar en
los foros y de una noticia de
bulma. Ten cuidado y no los uses a lo loco porque al cambiar
el nombre a un fichero, puede que sea el enlace de una
página html, o puede que le quites los permisos de
ejecución a un programa, etc.
#!/bin/bash
#
#===============================================================================
# Cambiamos recursivamente los permisos de los directorios a 755 y de
# los ficheros a 644
#===============================================================================
#
if [ $# -ne 1 ]
then
echo "Uso: "${0##*/}" ruta"
exit 0
fi
# Si queremos reducir permisos procesar primeros los ficheros, si no,
# al revés. Si vamos a reducir los permisos de los directorios y
# son tan restrictivos que no dejan entrar en ellos debemos usar la
# opción depth para ir desde las ramas hasta la raíz de la búsqueda.
# Si vamos aumentar los permisos de directorios que no nos dejen entrar
# en ellos primero poner chmod -R 755 * y luego usar permisos.sh
#
# Se puede usar la opción execdir por seguridad, aunque puede ser más
# lento porque agrupa ficheros como mucho por directorio.
find $1 -depth -type d -exec chmod 755 "{}" \+
find $1 -depth -type f -exec chmod 644 "{}" \+
# Otra manera (con las opción print0 y -0 no hay problemas con los
# nombres con espacios):
# find -type f -print0 | xargs -0 chmod 644
#!/bin/bash
#===============================================================================
# Renombra recursivamente los espacios de los archivos a guion bajo.
#===============================================================================
#
# En este guión está resuelto con la opción -depth y separando la
# ruta del nombre del fichero y luego modificando solo el nombre del
# fichero. Si solo usamos -depth no vale porque sed modifica tambié
# la ruta. Si solo usamos el separar ruta y nombre no funciona porque se
# modifican las rutas más cercanas a la raíz de búsqueda y luego los
# nombres de las ramas ya no coinciden con los que find encontró primero.
if [ $# -ne 1 ]
then
echo "Uso: "${0##*/}" ruta"
exit 0
fi
find $1 -depth -name '* *' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/ /_/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
#!/bin/bash
#
#===============================================================================
# Cambia recursivamente a n y a las vocales sin acentuar (minusculas
# y mayusculas).
#===============================================================================
#
# Este guión está resuelto con la opción -depth y separando la
# ruta del nombre del fichero y luego modificando solo el nombre del
# fichero. Si solo usamos -depth no vale porque sed modifica también
# la ruta. Si solo usamos el separar ruta y nombre no funciona porque se
# modifican las rutas más cercanas a la raíz de búsqueda y luego los
# nombres de las ramas ya no coinciden con los que find encontró primero.
#
if [ $# -ne 1 ]
then
echo "Uso: "${0##*/}" ruta"
exit 0
fi
find $1 -depth -name '*ñ*'| while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/ñ/n/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*Ñ*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/Ñ/N/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*á*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/á/a/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*é*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/é/e/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*í*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre"
sed 's/í/i/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*ó*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/ó/o/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*ú*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/ú/u/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*Á*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/Á/A/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*É*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre"
sed 's/É/E/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*Í*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/Í/I/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*Ó*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/Ó/O/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
find $1 -depth -name '*Ú*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
nuevonombre=`echo "$nombre" | sed 's/Ú/U/g'`
mv "${ruta}/""$nombre" "${ruta}/""$nuevonombre"
done
#!/bin/bash
#
#===============================================================================
# Cambia recursivamente de mayúsculas a minúsculas. No cambia la ñ, ni las
# tildes, pasar primero acentos_y_enes.sh
#===============================================================================
#
if [ $# -ne 1 ]
then
echo "Uso: "${0##*/}" ruta"
exit 0
fi
# Se debe usar la opción -execdir en vez de -exec y la opción -depth.
#
# -depth hace que se procese de las ramas a la raíz de la búsqueda.
# -execdir hace que rename se ejecute en el directorio que contiene los
# ficheros usando rutas relativas (con el prefijo ./) en vez de absolutas.
#
# Si solo usamos -depth no funciona porque rename, igual que en la
# versión original de espacio.sh, cambia todas las letras de la ruta del
# fichero y como todavía no se ha cambiado el nombre de los directorios
# superiores luego no puede renombrarlos. Con permisos.sh vale solo con
# esta opción porque chmod modifica los permisos del fichero o directorio
# y no modifica nada de la ruta absoluta. Si solo usamos -exec tampoco
# funciona porque cambia el nombre de los directorios superiores y luego
# no puede cambiar el nombre de los directorios o ficheros inferiores
# puesto que cambió su ruta absoluta. Si solo usamos -execdir tampoco
# funciona porque cambia el nombre de los directorios superiores y
# luego no puede ir a los directorios más profundos que encontró en
# la búsqueda inicial para ejecutar allí el comando con ruta relativa.
find $1 -depth -execdir rename -v 'tr/A-Z/a-z/' "{}" \+
Ficheros con nombres problemáticos de ejemplo (0,4 MiB)
#!/bin/bash
#
#===============================================================================
# Quita recursivamente los caracteres problemáticos de los nombres de fichero.
# Ejecutarlo varias veces, a la primera puede que no los sustituya todos.
# Por ejemplo si find busca el - inicial y el nombre tiene \ find no muestra
# bien el nombre porque piensa que \ está escapando al siguiente carácter. Solo
# lo muestra bien cuando busca específicamente la \. Usa find y sed y cada uno
# interpreta los caracteres problemáticos de diferente manera por lo que tuve que
# hacer el guión un poco empíricamente. Hay un directorio con ficheros
# problemáticos de ejemplo. Listarlos con ls -pQR | less
#
# No quita el carácter de nueva línea ni el tabulador. Existe el programa detox
# para lo mismo.
#===============================================================================
#
# http://www.controlledvocabulary.com/imagedatabases/filename_limits.html
# http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
# https://llantones.bitbucket.io/nombres_raros.zip
# caracteres permitidos:
# 1234567890
# abcdefghijklmnopqrstuvwxyz
# .
# _
#
# no puede empezar por . (porque estonces es oculto)
# no puede haber espacios, ni ñ, ni tildes.
# se pueden usar mayúsculas pero para la máxima compatibilidad entre sistemas
# no usarlas.
# El - da problemas con ISO-9660 y en algunos servidores web.
# Longitud: 8 caracteres más el punto más la extensión daría bastante
# portabilidad.
# Algo razonable actualmente sería 64 o 100 como mucho.
# GNU/Linux acepta 255 caracteres para nombre y 4096 para ruta completa. Hasefroch
# creo que acepta 255 para nombre incluyendo la ruta completa.
# 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
# aaaaaaaa10bbbbbbbb20cccccccc30dddddddd40eeeeeeee50ffffffff60gggggggg70hhhhhhhh80iiiiiiii90jjjjjjj100
# 20140631_practica_de_quimica_fisica_nivel_1-ano_0_bis
# Para este código genérico de modificación de nombres de fichero:
#
#find $1 -depth -name '*ñ*' | while read fichero
#do nuevofichero=`echo "$fichero" | sed 's/ñ/n/g'`
#mv "$fichero" "$nuevofichero"
#done
#
# es necesario ejecutarlo varias veces porque find realiza una búsqueda
# inicial con los nombres originales y conforme van cambiando los nombres
# de los directorios llega un momento en que ya no se encuentran los
# ficheros puesto que sus rutas absolutas han cambiado." Se podría
# resolver usando la opción -execdir en vez de -exec y la opción
# -depth, pero no he podido poner todos los comandos en una tubería. En
# minusculas.sh sí funciona: find $1 -depth -execdir rename 'tr/A-Z/a-z/'
# "{}" \+
#
# En este guión está resuelto con la opción -depth y separando la
# ruta del nombre del fichero y luego modificando solo el nombre del
# fichero. Si solo usamos -depth no vale porque sed modifica también
# la ruta. Si solo usamos el separar ruta y nombre no funciona porque se
# modifican las rutas más cercanas a la raíz de búsqueda y luego los
# nombres de las ramas ya no coinciden con los que find encontró primero.
#
if [ $# -ne 1 ]
then
echo "Uso: "${0##*/}" ruta"
exit 0
fi
# 35 caracteres problemáticos los sustituimos por _. El - solo sutituimos si
# es el primer carácter del nombre, si está en medio o final no da problemas.
# El primer carácter es el - al principio de nombre, lo sustituimos por _:
find $1 -depth -iname '-*' | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
sust="_"
nuevonombre=$(echo "${nombre}" | sed "s/-/${sust}/")
# comprobamos si ya existe algún fichero o directorio y si es así le añadimos
# más _ al nombre:
while [ -e $1/"${nuevonombre}" ]
do
sust=${sust}_
nuevonombre=$(echo "${nombre}" | sed "s/-/${sust}/")
done
mv -v "${ruta}/${nombre}" "${ruta}/${nuevonombre}"
done
# los 32 siguientes son:
# 123 45678901234567890123456789012
# 10 20 30
# con bucle for no funciona para ! + ª así que usamos while:
find $1 -depth -iname '*[()[\]{}<>*+=!?"·$%&^ºª,;:|#~`@¡¿¤]*' | while read fichero
do
ruta="${fichero%/*}"
nombre=${fichero##*/}
# Para que sed sustituya [ y ] hay que ponerlos juntos al principio de la
# sustitución juntos. Fuente: http://www.grymoire.com/Unix/Regular.html#uh-6
sust="_"
nuevonombre=$(echo "${nombre}" | sed "s/[][(){}<>*+=\!?\"·$%&^ºª,;:|#~\`@¡¿¤]/${sust}/g")
# comprobamos si ya existe algún fichero o directorio y si es así le añadimos
# más _ al nombre:
while [ -e $1/"${nuevonombre}" ]
do
sust=${sust}_
nuevonombre=$(echo "${nombre}" | sed "s/[][(){}<>*+=\!?\"·$%&^ºª,;:|#~\`@¡¿¤]/${sust}/g")
done
mv -v "${ruta}/${nombre}" "${ruta}/${nuevonombre}"
done
# el 34 y el 35 los ponemos a parte porque o dan problemas en while, en find o
# o en sed y requieren un tratamiento personalizado.
# , el 34:
# con el bucle while no funciona, tiene que ser con for:
for fichero in $(find $1 -depth -name '*\\*')
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
sust="_"
nuevonombre=$(echo "${nombre}" | sed "s/[\]/${sust}/g")
# comprobamos si ya existe algún fichero o directorio y si es así le añadimos
# más _ al nombre:
while [ -e $1/"${nuevonombre}" ]
do
sust=${sust}_
nuevonombre=$(echo "${nombre}" | sed "s/[\]/${sust}/g")
done
mv -v "${ruta}/${nombre}" "${ruta}/${nuevonombre}"
done
# y el 35:
find $1 -depth -iname "*'*" | while read fichero
do
ruta="${fichero%/*}"
nombre="${fichero##*/}"
sust="_"
nuevonombre=$(echo "${nombre}" | sed "s/'/${sust}/g")
# comprobamos si ya existe algún fichero o directorio y si es así le añadimos
# más _ al nombre:
while [ -e $1/"${nuevonombre}" ]
do
sust=${sust}_
nuevonombre=$(echo "${nombre}" | sed "s/'/${sust}/g")
done
mv -v "${ruta}/${nombre}" "${ruta}/${nuevonombre}"
done
Los guiones anteriores más
estos (recuerda que pueden aplicarse a un directorio solo o
recursivamente):