Minúsculas todos los directorios bajo un directorio

12

Quiero poner en minúscula el nombre de cada directorio debajo de un directorio. ¿Con qué comandos puedo hacer eso?

erkangur
fuente

Respuestas:

10

¿Todos los directorios en un nivel, o recursivamente?

Zsh

En un nivel:

autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'

Recursivamente:

zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'

Explicaciones: zmvcambia el nombre de los archivos que coinciden con un patrón de acuerdo con el texto de reemplazo dado. -o-ipasa la -iopción a cada mvcomando debajo del capó (ver más abajo). En el texto de reemplazo, $1, $2, etc, son los grupos sucesivos entre paréntesis en el patrón. **significa todos los directorios (sub) *, recursivamente. El final (/)no es un grupo entre paréntesis, sino un calificador global que significa que solo coinciden con los directorios. ${2:l}Convierte $2a minúsculas.

Portátil

En un nivel:

for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done

El final /restringe la correspondencia con los directorios y mv -ihace que solicite confirmación en caso de una colisión. Elimine -ipara sobrescribir en caso de una colisión, y use yes n | for …. para que no se le solicite y no realice ningún cambio de nombre que colisionaría.

Recursivamente:

find root/* -depth -type d -exec sh -c '
    t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
    [ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;

El uso de -depthasegura que los directorios profundamente anidados se procesen antes que sus antepasados. El procesamiento del nombre depende de que haya un /; si desea llamar a operar en el directorio actual, use ./*(adaptar el script de shell para hacer frente .o *se deja como un ejercicio para el lector).

Perl renombrar

Aquí utilizo el script de cambio de nombre de Perl que Debian y Ubuntu incluyen /usr/bin/prename(normalmente también disponible rename). En un nivel:

rename 's!/([^/]*/?)$!\L/$1!' root/*/

Recursivamente, con bash ≥4 o zsh:

shopt -s globstar  # only in bash
rename 's!/([^/]*/?)$!\L/$1!' root/**/*/

Recursivamente, portablemente:

find root -depth -type d -exec rename -n 's!/([^/]*/?)$!\L/$1!' {} +
Gilles 'SO- deja de ser malvado'
fuente
Al menos en OS X, esto fallará si algún directorio ya está en minúscula: mv -i a adé "mv: cambie el nombre de a a / a: argumento no válido".
Janus
@ Janus: Correcto, recibes un mensaje de error, que es feo (aunque inofensivo en la línea de comando). Pero de todos modos, debería haber usado zmv, que se encarga de este caso.
Gilles 'SO- deja de ser malvado'
Luego descubrí -execdirque es increíble: unix.stackexchange.com/questions/5412/… Luego descubrí que tiene algo de PATHlocura y estaba triste :-(
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
4

No hay un solo comando que haga eso, pero puedes hacer algo como esto:

for fd in */; do
  #get lower case version
  fd_lower=$(printf %s "$fd" | tr A-Z a-z)
  #if it wasn't already lowercase, move it.
  [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"
done

Si necesita que sea robusto, debe tener en cuenta cuando ya hay dos directorios que difieren solo en el caso.

Como una línea:

for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done
Shawn J. Goff
fuente
Esta mitad mostró a través de mi escritura del (básicamente la misma) sugerencia:for file in * ; do if [ -d "$file" ] ; then dest="$(echo $file | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/)" ; [ "$dest" != "$file" ] && mv "$file" "$dest" ; fi ; done
frabjous
Estaba tratando de resolverlo find -type d, pero no pude entenderlo
Michael Mrozek
1
Mi instinto habría sido hacerlo for fd in */;, evitando así la necesidad de verificar si era un directorio, pero no tengo idea si este instinto fue bueno.
Steven D
Sí, para ... * / sería mejor.
Shawn J. Goff
1
@Shawn: trya espera un intervalo, por lo tr '[A-Z]' '[a-z]'traduce [a [y ]a ]de paso. Esto es inútil pero inofensivo; sin embargo, sin las comillas, el shell expandiría los corchetes si hubiera un archivo con un nombre de una letra mayúscula en el directorio actual.
Gilles 'SO- deja de ser malvado'
0

Tomé esto como un desafío de una línea :) Primero, establezca un caso de prueba:

$ for d in foo Bar eVe; do mkdir -p dcthis/$d; touch dcthis/a${d}.txt; done
$ ls dcthis/
Bar     aBar.txt    aeVe.txt    afoo.txt    eVe     foo

Yo uso findpara detectar los directorios con letras mayúsculas y luego en minúsculas a través de . Supongo que usar es un poco hack-ish, pero mi cabeza siempre explota cuando trato de escapar de las cosas directamente.sh -c 'mv {} echo {} | tr [:upper:] [:lower:]'sh -cfind

$ (cd dcthis && find . -maxdepth 1 -type d -path '*[A-Z]*' -exec sh -c 'mv {} `echo {} | tr [:upper:] [:lower:]`' \;)
$ ls dcthis/
aBar.txt    aeVe.txt    afoo.txt    bar     eve     foo

Tenga cuidado: ¡esta solución no comprueba si la reducción de la capa conduce a colisiones!

Janus
fuente
la comprobación de colisiones es fácil: mv -i. Un problema mayor es que no ha utilizado las comillas adecuadas, por lo que su comando fallará si hay caracteres especiales (espacios en blanco o \[*?) en cualquier parte del nombre. No tiene sentido usarlo a findmenos que recurra, y luego necesita find -depthy -pathdebe ser -name. Vea mi respuesta para ejemplos de trabajo.
Gilles 'SO- deja de ser malvado'
@Giles. ¡Gracias! Te vi mv -idespués de escribir esto. Buen punto con la cita ...
Janus
0

find -execdirEl | rebautizar

Esta sería la mejor manera de hacerlo si no fuera por la locura de la ruta relativa, ya que evita que Perl regex fu solo actúe en el nombre base:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;

-execdirprimero cds en el directorio antes de ejecutar solo en el nombre base.

Desafortunadamente, no puedo deshacerme de esa PATHparte de piratería, se find -execdirniega a hacer nada si tiene una ruta relativa en PATH...: /ubuntu/621132/why-using-the-execdir-action- is-insecure-for-directory-which-is-in-the-path / 1109378 # 1109378

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
fuente