Réponses:
Tous les répertoires à un niveau, ou récursivement?
À un niveau:
autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'
Récursivement:
zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'
Explications: zmv
renomme les fichiers correspondant à un modèle en fonction du texte de remplacement donné. -o-i
passe l' -i
option à chaque mv
commande sous le capot (voir ci-dessous). Dans le texte de remplacement, $1
, $2
, etc, sont les groupes successifs dans le parenthésées modèle. **
signifie tous les répertoires (sous) *, récursivement. La finale (/)
n'est pas un groupe entre parenthèses mais un qualificatif glob qui signifie ne correspondre qu'aux répertoires. ${2:l}
convertit $2
en minuscules.
À un niveau:
for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done
La finale /
limite la correspondance aux répertoires et lui mv -i
fait demander une confirmation en cas de collision. Retirez le -i
pour écraser en cas de collision et utilisez yes n | for …
. pour ne pas être invité et ne pas effectuer de changement de nom qui entrerait en collision.
Récursivement:
find root/* -depth -type d -exec sh -c '
t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
[ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;
L'utilisation de -depth
assure que les répertoires profondément imbriqués sont traités avant leurs ancêtres. Le traitement du nom repose sur l'existence d'un /
; si vous voulez appeler operer dans le répertoire courant, utilisez ./*
(l'adaptation du script shell pour faire face .
ou *
est laissé comme exercice pour le lecteur).
Ici, j'utilise le script de changement de nom Perl /usr/bin/prename
fourni par Debian et Ubuntu (généralement également disponible rename
). À un niveau:
rename 's!/([^/]*/?)$!\L/$1!' root/*/
Récursivement, avec bash ≥4 ou zsh:
shopt -s globstar # only in bash
rename 's!/([^/]*/?)$!\L/$1!' root/**/*/
De manière récursive, portable:
find root -depth -type d -exec rename -n 's!/([^/]*/?)$!\L/$1!' {} +
-execdir
ce qui est génial: unix.stackexchange.com/questions/5412/… J'ai ensuite trouvé qu'il y avait de la PATH
folie et était triste :-(
Il n'y a pas une seule commande qui fera cela, mais vous pouvez faire quelque chose comme ceci:
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 vous en avez besoin pour être robuste, vous devez tenir compte du fait qu'il existe déjà deux répertoires qui ne diffèrent qu'en cas.
En une ligne:
for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done
for file in * ; do if [ -d "$file" ] ; then dest="$(echo $file | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/)" ; [ "$dest" != "$file" ] && mv "$file" "$dest" ; fi ; done
find -type d
, mais je n'arrivais pas à comprendre
for fd in */;
, évitant ainsi la nécessité de vérifier s'il s'agissait d'un répertoire, mais je n'ai aucune idée si cet instinct était bon.
tr
attend déjà une gamme, donc tr '[A-Z]' '[a-z]'
traduit [
à [
et ]
à ]
en passant. C'est inutile mais inoffensif; cependant, sans les guillemets, le shell étendrait les crochets s'il y avait un fichier avec un nom à une lettre majuscule dans le répertoire courant.
J'ai pris cela comme un défi à une ligne :) Commencez par établir un scénario de test:
$ 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
J'utilise find
pour repérer les répertoires avec des lettres majuscules, puis les télécharger via . Je suppose que l'utilisation est un peu hack-ish, mais ma tête explose toujours lorsque j'essaie d'échapper aux choses directement.sh -c 'mv {}
echo {} | tr [:upper:] [:lower:]
'sh -c
find
$ (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
Attention: cette solution ne vérifie pas si la rétrogradation entraîne des collisions!
mv -i
. Un problème plus important est que vous n'avez pas utilisé de guillemets appropriés, donc votre commande échouera s'il y a des caractères spéciaux (espaces ou \[*?
) n'importe où dans le nom. Il n'y a aucun intérêt à l'utiliser à find
moins que ce ne soit récursif, et alors vous en avez besoin find -depth
et -path
devez l'être -name
. Voir ma réponse pour des exemples de travail.
mv -i
après avoir écrit ceci. Bon point avec la citation ...
find -execdir
| Renommer
Ce serait la meilleure façon de le faire sans la folie relative du chemin, car cela évite que Perl regex fu n'agisse que sur le nom de base:
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;
-execdir
d'abord cd
dans le répertoire avant d'exécuter uniquement sur le nom de base.
Malheureusement, je ne peux pas me débarrasser de cette PATH
partie de piratage, find -execdir
refuse de faire quoi que ce soit si vous avez un chemin relatif dans PATH
: /ubuntu/621132/why-using-the-execdir-action- is-insecure-for-directory-which-is-in-the-path / 1109378 # 1109378
mv -i a a
donnez "mv: rename a à a / a: Argument invalide".