Inicio

Versión 1.0 - julio de 2005

Versión 2.0 - julio de 2015 - Mejorados algunos guiones y aņadidos nuevos

Guiones Bash

Cambiar permisos, quitar espacios, tildes y ñ y pasar a minúsculas

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.

Permisos


#!/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


Espacios


#!/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


Acentos y eñes


#!/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

Minúsculas


#!/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/' "{}" \+


Caracteres problemáticos

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 


Más guiones

Los guiones anteriores más estos (recuerda que pueden aplicarse a un directorio solo o recursivamente):

Inicio

HTML5 Powered