Je dois renommer une arborescence de dossiers complète de manière récursive afin qu'aucune lettre majuscule n'apparaisse nulle part (c'est du code source C++, mais cela ne devrait pas avoir d'importance). Points bonus pour ignorer les fichiers/dossiers de contrôle CVS et SVN. La méthode préférée serait un script Shell, puisque Shell devrait être disponible sur n’importe quelle machine Linux.
Il y avait des arguments valables concernant les détails du renommage du fichier.
Je pense que les fichiers avec les mêmes noms en minuscules devraient être écrasés, c'est le problème de l'utilisateur. En cas d'extraction sur un système de fichiers ignorant la casse, le premier sera écrasé par le dernier.
Je considérerais les caractères A-Z et les transformerais en a-z, tout le reste n’appelant que des problèmes (au moins avec le code source).
Le script serait nécessaire pour exécuter une compilation sur un système Linux. Je pense donc que les modifications apportées aux fichiers de contrôle CVS ou SVN devraient être omises. Après tout, il ne s'agit que d'une sortie à zéro. Peut-être qu'une "exportation" est plus appropriée.
Une version concise utilisant la commande "rename"
.
find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
Cela évite les problèmes de renommage des répertoires avant les fichiers et d'essayer de déplacer des fichiers dans des répertoires non existants (par exemple, "A/A"
dans "a/a"
).
Ou, une version plus détaillée sans utiliser "rename"
.
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.
Ce dernier permet plus de flexibilité avec la commande de déplacement (par exemple, "svn mv"
).
plus petit encore j'aime bien
rename 'y/A-Z/a-z/' *
Sur les systèmes de fichiers insensibles à la casse tels que HFS + de OS X, vous voudrez ajouter l'indicateur -f
rename -f 'y/A-Z/a-z/' *
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Essayez simplement de suivre si vous n'avez pas à vous soucier de l'efficacité.
Zip -r foo.Zip foo/*
unzip -LL foo.Zip
La plupart des réponses ci-dessus sont dangereuses car elles ne traitent pas de noms contenant des caractères impairs. Votre pari le plus sûr pour ce genre de choses est d’utiliser l’option -print0 de find, qui mettra fin aux noms de fichiers contenant ascii NUL au lieu de\n. Ici, je soumets ce script, qui modifie uniquement les fichiers et non les noms de répertoires afin de ne pas confondre find.
find . -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "$0")/$(basename "$0");
d=$(dirname "$0")/$(basename "$0"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'
Je l'ai testé et cela fonctionne avec les noms de fichiers contenant des espaces, toutes sortes de citations, etc. Ceci est important car si vous exécutez, en tant que root, l'un de ces autres scripts sur une arborescence contenant le fichier créé par:
touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash
... bien devinez quoi ...
Cela fonctionne si vous avez déjà ou configurez rename command (par exemple, via brew install in Mac):
rename --lower-case --force somedir/*
Homme, vous aimez trop compliquer les choses ..
renommer 'y/A-Z/a-z /' *
Cela fonctionne sur CentOS/Redhat ou d'autres distributions sans le script Perl rename
:
for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done
Source: https://linuxconfig.org/rename-all-files-from-uppercase-to-lowercase-characters
(Dans certaines distributions, la commande par défaut rename
provient de util-linux, et il s'agit d'un outil différent et incompatible.)
La question initiale demandait d'ignorer les répertoires SVN et CVS, ce qui peut être fait en ajoutant -Prune à la commande find. E.g pour ignorer CVS:
find . -name CVS -Prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print
[edit] J'ai essayé cela, et intégrer la traduction en minuscule à la découverte n'a pas fonctionné pour des raisons que je ne comprends pas vraiment. Alors, modifiez ceci pour:
$> cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
$> chmod u+x tolower
$> find . -name CVS -Prune -o -exec tolower '{}' \;
Ian
Voici ma solution sous-optimale, utilisant 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
Edit: J'ai apporté quelques modifications sur la base des suggestions faites jusqu'à présent. Désormais, tous les dossiers sont renommés correctement, mv ne pose pas de questions lorsque les autorisations ne correspondent pas et les dossiers CVS ne sont pas renommés (les fichiers de contrôle CVS de ce dossier sont toujours renommés, malheureusement).
Edit: Puisque "find -depth" et "find | sort -r" renvoient la liste des dossiers dans un ordre utilisable pour le changement de nom, j'ai préféré utiliser "-depth" pour la recherche de dossiers.
Utiliser le fixateur de nom de fichier de Larry Wall
$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
$was = $_;
eval $op;
die $@ if $@;
rename($was,$_) unless $was eq $_;
}
c'est aussi simple que
find | fix 'tr/A-Z/a-z/'
(où fix est bien sûr le script ci-dessus)
Ceci est un petit script Shell qui répond à vos attentes:
root_directory="${1?-please specify parent directory}"
do_it () {
awk '{ lc= tolower($0); if (lc != $0) print "mv \"" $0 "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it
Notez l'action-profondeur dans la première recherche.
Le précédent message fonctionnera parfaitement sans modifications ou avec quelques ajustements pour des cas simples, mais vous voudrez peut-être prendre en compte certaines situations avant d'exécuter le changement de nom de lot:
Que devrait-il se passer si vous avez deux noms ou plus au même niveau dans la hiérarchie des chemins qui ne diffèrent que par cas, comme ABCdef
, abcDEF
et aBcDeF
? Le script de changement de nom doit-il abandonner ou simplement avertir et continuer?
Comment définissez-vous les minuscules pour les noms non US-ASCII? Si de tels noms peuvent être présents, faut-il vérifier d'abord et exclure le passage?
Si vous exécutez une opération de changement de nom sur des copies de travail CVS ou SVN, vous pouvez endommager la copie de travail si vous modifiez la casse des noms de fichier ou de répertoire. Le script doit-il également rechercher et ajuster les fichiers administratifs internes tels que .svn/entries ou CVS/Entries?
Avec MacOS,
Installez le paquetage rename
,
brew install rename
Utilisation,
find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
Cette commande trouve tous les fichiers avec une extension *.py
et convertit les noms de fichiers en minuscules.
`f` - forces a rename
Par exemple,
$ 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
Je voudrais atteindre pour python dans cette situation, pour éviter avec optimisme d'assumer des chemins sans espaces ni barres obliques. J'ai également constaté que python2
a tendance à être installé dans plus d'endroits 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])
Dans OSX, mv -f affiche l'erreur "même fichier", aussi je renomme deux fois.
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
L’approche la plus simple que j’ai trouvée sur MacOSX consistait à utiliser le package de changement de nom de http://plasmasturm.org/code/rename/ :
brew install rename
rename --force --lower-case --nows *
--force Renommer même lorsqu'un fichier avec le nom de destination existe déjà.
--lower-case Convertit les noms de fichier en minuscules.
--nows Remplace toutes les séquences d'espaces dans le nom du fichier par des caractères de soulignement simples.
for f in `find -depth`; do mv ${f} ${f,,} ; done
find -depth
imprime chaque fichier et répertoire, avec le contenu d'un répertoire imprimé avant le répertoire lui-même. ${f,,}
met en minuscule le nom du fichier.
Pas portable, seulement Zsh, mais assez concis.
Tout d’abord, assurez-vous que zmv
est chargé.
autoload -U zmv
Assurez-vous également que extendedglob
est activé:
setopt extendedglob
Alors utilisez:
zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'
Récursivement en minuscule les fichiers et les répertoires dont le nom n'est pasCVS.
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh
Je n'ai pas essayé les scripts plus élaborés mentionnés ici, mais aucune des versions en ligne de commande simples ne fonctionnait pour moi sur mon NAS Synology. rename
n'est pas disponible et de nombreuses variantes de find
échouent, car il semble coller à l'ancien nom du chemin déjà renommé (par exemple, s'il trouve ./FOO
suivi de ./FOO/BAR
, renommer ./FOO
en ./foo
continuera de lister ./FOO/BAR
même si ce chemin n'est plus valide). La commande ci-dessus a fonctionné pour moi sans aucun problème.
Ce qui suit est une explication de chaque partie de la commande:
find . -depth -name '*[A-Z]*'
Cela trouvera n'importe quel fichier du répertoire en cours (changez .
en n'importe quel répertoire que vous voulez traiter), en utilisant une recherche en profondeur d'abord (par exemple, il listera ./foo/bar
avant ./foo
), mais uniquement pour les fichiers contenant une majuscule. Le filtre -name
s'applique uniquement au nom du fichier de base, pas au chemin d'accès complet. Donc, ceci listera ./FOO/BAR
mais pas ./FOO/bar
. C'est bon, car nous ne voulons pas renommer ./FOO/bar
. Nous voulons cependant renommer ./FOO
, mais celui-ci est répertorié plus tard (c’est pourquoi -depth
est important).
Cette commande en elle-même est particulièrement utile pour rechercher les fichiers que vous souhaitez renommer. Utilisez-le après la commande rename complete pour rechercher des fichiers qui n'ont toujours pas été remplacés en raison de collisions ou d'erreurs de noms de fichiers.
sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'
Cette partie lit les fichiers générés par find
et les formate dans une commande mv
en utilisant une expression régulière. L'option -n
empêche sed
d'imprimer l'entrée et la commande p
dans l'expression rationnelle de remplacement remplace le texte remplacé.
La regex elle-même consiste en deux captures: la partie jusqu'à la dernière/(qui est le répertoire du fichier) et le nom du fichier lui-même. Le répertoire est laissé intact, mais le nom du fichier est transformé en minuscule. Donc, si find
génère ./FOO/BAR
, il deviendra mv -n -v -T ./FOO/BAR ./FOO/bar
. L'option -n
de mv
permet de s'assurer que les fichiers en minuscules existants ne sont pas écrasés. L'option -v
permet à mv
de générer chaque modification apportée (ou ne s'effectue pas - si ./FOO/bar
existe déjà, il génère quelque chose comme ./FOO/BAR -> ./FOO/BAR
, en notant qu'aucune modification n'a été apportée). Le -T
est très important ici - il traite le fichier cible comme un répertoire. Cela garantira que ./FOO/BAR
ne sera pas déplacé dans ./FOO/bar
si ce répertoire existe.
Utilisez ceci avec find
pour générer une liste de commandes qui seront exécutées (pratique pour vérifier ce qui sera fait sans le faire réellement)
sh
Cette jolie explicite. Il route toutes les commandes mv
générées vers l'interpréteur Shell. Vous pouvez le remplacer par bash
ou tout shell de votre choix.
( 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
Commencez par renommer les répertoires de bas en haut sort -r (où -depth n'est pas disponible), puis les fichiers . Ensuite, grep -v/CVS au lieu de find ...- Prune parce que c'est plus simple . Pour les grands répertoires, for f in ... peut déborder de la mémoire tampon du shell . Utilisez find ... | en lisant pour éviter cela.
Et oui, cela encombrera des fichiers qui ne diffèrent que par le cas ...
Si vous utilisez Arch Linux , vous pouvez installer rename
package à partir de AUR
qui fournit la commande renamexm
en tant que /usr/bin/renamexm
exécutable et son manual page.
C'est un outil vraiment puissant pour renommer rapidement des fichiers et des répertoires.
rename -l Developers.mp3 # or --lowcase
rename -u developers.mp3 # or --upcase, long option
-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
Vous pouvez récupérer le fichier exemple Developers.mp3 à partir de ici , si nécessaire;)
J'avais besoin de faire cela sur une configuration de cygwin sous Windows 7 et j'ai constaté que j'avais des erreurs de syntaxe avec les suggestions ci-dessus que j'avais essayées (bien que j'aie peut-être manqué une option de travail), mais cette solution directement depuis ubutu forums a fonctionné hors de la boîte :-)
ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done
(nb j’avais précédemment remplacé les espaces par des traits de soulignement en utilisant
for f in *\ *; do mv "$f" "${f// /_}"; done
Longy But "Fonctionne sans surprises ni installations"
Ce script traite les noms de fichiers avec des espaces, des guillemets, d’autres inhabituels caractères et Unicode, fonctionne sur les systèmes de fichiers non sensibles à la casse et la plupart des environnements Unix-y sur lesquels bash et awk sont installés (c'est-à-dire presque tous). Il signale également les collisions éventuelles (en laissant le nom du fichier en majuscule) et, bien sûr, renomme les fichiers et les répertoires et fonctionne de manière récursive. Enfin, il est très adaptable: vous pouvez modifier la commande find pour cibler les fichiers/répertoires de votre choix et Tweak awk pour effectuer d’autres manipulations de noms. Notez que par "gère Unicode", je veux dire qu'il convertira leur cas (ne les ignorez pas comme les réponses qui utilisent 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(\$0)}")
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 {} +
Références
Mon script est basé sur ces excellentes réponses:
https://unix.stackexchange.com/questions/9496/looping-through-files-with-spaces-in-the-names
Cela fonctionne bien aussi sur macOS:
Ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
Ce n'est pas exactement ce que le PO a demandé, mais ce que j'espérais trouver sur cette page:
Une version "slugify" pour renommer les fichiers afin qu'ils soient similaires aux URL
(c’est-à-dire inclure uniquement des caractères alphanumériques, des points et des tirets):
rename "s/[^a-zA-Z0-9\.]+/-/g" filename
Personne ne suggère de composition?
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
Sous Windows, les émulations telles que git bash peuvent échouer car Windows n'est pas sensible à la casse sous le capot. Pour ceux-là, ajoutez une étape qui mv le fichier est d'abord un autre nom, comme "$ old.tmp", puis à $ new.