Come rinominare tutte le cartelle e i file in minuscolo su Linux?
Domanda
Devo rinominare un albero di cartelle completo in modo ricorsivo in modo che nessuna lettera maiuscola appaia da nessuna parte (è il codice sorgente C ++, ma non dovrebbe importare). Punti bonus per ignorare file / cartelle di controllo CVS e SVN. Il modo preferito sarebbe uno script di shell, poiché la shell dovrebbe essere disponibile in qualsiasi box Linux.
C'erano alcuni argomenti validi sui dettagli della ridenominazione del file.
-
Penso che i file con gli stessi nomi in minuscolo debbano essere sovrascritti, è un problema dell'utente. Se estratto su un file system che ignora i casi, sovrascriverà anche il primo con quest'ultimo.
-
Vorrei considerare i caratteri dalla A alla Z e trasformarli in A-Z, tutto il resto richiede solo problemi (almeno con il codice sorgente).
-
Lo script sarebbe necessario per eseguire una build su un sistema Linux, quindi penso che le modifiche ai file di controllo CVS o SVN debbano essere omesse. Dopotutto, è solo un checkout. Forse un " export " è più appropriato.
Soluzione
Una versione sintetica che utilizza il comando " rinomina "
.
find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
Questo evita problemi con la ridenominazione delle directory prima dei file e il tentativo di spostare i file in directory inesistenti (ad es. " A / A "
in " a / a "
).
Oppure, una versione più dettagliata senza usare " rinominare "
.
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.
Quest'ultimo consente una maggiore flessibilità con il comando move (ad es. " svn mv "
).
Altri suggerimenti
più piccolo mi piace ancora
rename 'y/A-Z/a-z/' *
Su filesystem insensibili al maiuscolo / minuscolo come HFS + di OS X, ti consigliamo di aggiungere il flag -f
rename -f 'y/A-Z/a-z/' *
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Prova semplicemente a seguire se non hai bisogno di preoccuparti dell'efficienza.
zip -r foo.zip foo/*
unzip -LL foo.zip
La maggior parte delle risposte sopra è pericolosa perché non tratta i nomi che contengono caratteri dispari. La tua scommessa più sicura per questo tipo di cose è usare l'opzione -print0 di find, che terminerà i nomi dei file con ascii NUL invece di \ n. Qui invio questo script, che altera solo i file e non i nomi di directory per non confondere find.
find . -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash
")/$(basename "<*>");
d=$(dirname "<*>")/$(basename "<*>"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'
L'ho provato e funziona con nomi di file contenenti spazi, tutti i tipi di virgolette, ecc. Questo è importante perché se si esegue, come root, uno di quegli altri script su un albero che include il file creato da:
<*>... indovina un po '...
Funziona se hai già o impostato il comando rinomina (ad es. tramite brew install in Mac):
rename --lower-case --force somedir/*
Ragazzi, a voi ragazzi piace complicare troppo le cose ..
rinomina 'y / A-Z / a-z /' *
Funziona su CentOS / Redhat o altre distribuzioni senza lo script Perl rename
:
for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done
Fonte: https://linuxconfig.org/rename- tutti-files-da-maiuscolo-to-minuscoli caratteri-
(in alcune distro il comando rename
predefinito proviene da util-linux, e questo è uno strumento diverso e incompatibile)
Usando il fissatore di nomi di file di Larry Wall
$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
$was = Usando il fissatore di nomi di file di Larry Wall
find | fix 'tr/A-Z/a-z/'
è semplice come
<*>
(dove fix è ovviamente lo script sopra)
;
eval $op;
die $@ if $@;
rename($was, Usando il fissatore di nomi di file di Larry Wall
<*>
è semplice come
<*>
(dove fix è ovviamente lo script sopra)
) unless $was eq Usando il fissatore di nomi di file di Larry Wall
<*>
è semplice come
<*>
(dove fix è ovviamente lo script sopra)
;
}
è semplice come
<*>(dove fix è ovviamente lo script sopra)
La domanda originale chiedeva di ignorare le directory SVN e CVS, cosa che può essere fatta aggiungendo -prune al comando find. Es. Ignorare CVS:
find . -name CVS -prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print
[modifica] L'ho provato e l'incorporamento della traduzione in minuscolo all'interno della ricerca non ha funzionato per motivi che in realtà non capisco. Quindi, modificalo in:
<*>gt; cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
<*>gt; chmod u+x tolower
<*>gt; find . -name CVS -prune -o -exec tolower '{}' \;
Ian
Ecco la mia soluzione non ottimale, usando uno script Shell bash:
#!/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
Modifica: finora ho apportato alcune modifiche in base ai suggerimenti. Ora le cartelle sono tutte rinominate correttamente, mv non fa domande quando le autorizzazioni non corrispondono e le cartelle CVS non vengono rinominate (i file di controllo CVS all'interno di quella cartella sono ancora rinominati, sfortunatamente).
Modifica: poiché " find -depth " e "trova | ordina -r " entrambi restituiscono l'elenco delle cartelle in un ordine utilizzabile per rinominare, ho preferito usare " -depth " per la ricerca di cartelle.
Questo è uno script di shell piccolo che fa quello che hai richiesto:
root_directory="${1?-please specify parent directory}"
do_it () {
awk '{ lc= tolower(<*>); if (lc != <*>) print "mv \"" <*> "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it
Nota l'azione -depth nella prima ricerca.
I messaggi precedentemente pubblicati funzioneranno perfettamente fuori dagli schemi o con alcune regolazioni per casi semplici, ma ci sono alcune situazioni che potresti voler prendere in considerazione prima di eseguire la ridenominazione in batch:
-
Cosa dovrebbe succedere se nella gerarchia dei percorsi sono presenti due o più nomi allo stesso livello che differiscono solo per caso, come
ABCdef
,abcDEF
eabcdef
? Lo script di rinomina dovrebbe interrompersi o semplicemente avvisare e continuare? -
Come si definiscono i caratteri minuscoli per i nomi non US-ASCII? Se tali nomi potrebbero essere presenti, è necessario prima verificare e escludere il passaggio?
-
Se si sta eseguendo un'operazione di ridenominazione su copie di lavoro CVS o SVN, è possibile corrompere la copia di lavoro se si modifica il caso sui nomi di file o directory. Lo script dovrebbe anche trovare e modificare i file amministrativi interni come .svn / entry o CVS / Entries?
for f in `find -depth`; do mv ${f} ${f,,} ; done
find -depth
stampa ogni file e directory, con i contenuti di una directory stampati prima della directory stessa. $ {f ,,}
mette in minuscolo il nome del file.
Con MacOS,
Installa il pacchetto rinomina
,
brew install rename
Usa,
find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
Questo comando trova tutti i file con estensione * .py
e converte i nomi dei file in lettere minuscole.
`f` - forces a rename
Ad esempio,
$ 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
Non portatile, solo Zsh, ma piuttosto conciso.
Innanzitutto, assicurati che zmv
sia caricato.
autoload -U zmv
Inoltre, assicurati che extendedglob
sia attivo:
setopt extendedglob
Quindi utilizzare:
zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'
Per ricorrere in modo minuscolo ai file e alle directory in cui il nome non è CVS .
In OSX, mv -f mostra " stesso file " errore, quindi rinominerò due volte.
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
Avevo bisogno di farlo su una configurazione di Cygwin su Windows 7 e ho scoperto di avere errori di sintassi con i suggerimenti di cui sopra che ho provato (anche se potrei aver perso un'opzione funzionante), tuttavia questa soluzione è stata direttamente da forum ubutu ha funzionato a dovere :-)
ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done
(nb in precedenza avevo sostituito gli spazi bianchi con caratteri di sottolineatura usando
for f in *\ *; do mv "$f" "${f// /_}"; done
Vorrei cercare Python in questa situazione, per evitare di assumere in modo ottimistico percorsi senza spazi o barre. Ho anche scoperto che python2
tende ad essere installato in più punti rispetto a rinomina
.
#!/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])
Lungo ma " funziona senza sorprese & amp; Nessuna installazione "
Questo script gestisce nomi di file con spazi, virgolette, altri caratteri insoliti e Unicode, funziona su filesystem insensibili alle maiuscole e minuscole e sulla maggior parte degli ambienti Unix-y su cui è installato bash e awk ( cioè quasi tutti). Riporta anche eventuali collisioni (lasciando il nome del file in maiuscolo) e ovviamente rinomina sia i file che amp; directory e lavori ricorsivamente. Infine è altamente adattabile: puoi modificare il comando find per scegliere come target i file / le directory che desideri e puoi modificare awk per fare altre manipolazioni del nome. Tieni presente che da " gestisce Unicode " Voglio dire che convertirà effettivamente il loro caso (non ignorarli come risposte che usano 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(\<*>)}")
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 {} +
Riferimenti
La mia sceneggiatura si basa su queste eccellenti risposte:
https://unix.stackexchange.com/questions / 9496 / loop-through-files-con-spazi-in-the-nomi
L'approccio più semplice che ho trovato su MacOSX era l'uso del pacchetto di rinomina da http://plasmasturm.org/code / rinomina / :
brew install rename
rename --force --lower-case --nows *
--force Rinomina anche quando esiste già un file con il nome di destinazione.
--lower-case Converte i nomi dei file in tutte le lettere minuscole.
--nows Sostituisce tutte le sequenze di spazi bianchi nel nome file con singoli caratteri di sottolineatura.
( 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
Prima rinominare le directory dal basso verso l'alto sort -r (dove -depth non è disponibile), quindi i file. Quindi grep -v / CVS invece di trova ...- prugna perché è più semplice. Per directory di grandi dimensioni, per f in ... può sovraccaricare alcuni buffer di shell. Usa trova ... | mentre leggi per evitarlo.
E sì, questo bloccherà i file che differiscono solo nel caso ...
Rinomina slugify (regex)
Non esattamente quello che l'OP ha richiesto, ma quello che speravo di trovare in questa pagina:
Un "slugify" versione per rinominare i file in modo che siano simili agli URL
(ovvero include solo caratteri alfanumerici, punti e trattini):
rename "s/[^a-zA-Z0-9\.]+/-/g" filename
Se si utilizza Arch Linux , è possibile installare rinomina
pacchetto da AUR
che fornisce il comando renamexm
come / usr / bin / renamexm
eseguibile e una sua manuale insieme ad essa.
È uno strumento davvero potente per rinominare rapidamente file e directory.
Converti in minuscolo
rename -l Developers.mp3 # or --lowcase
Converti in maiuscole
rename -u developers.mp3 # or --upcase, long option
Altre opzioni
-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
Puoi recuperare il file Developers.mp3 di esempio da qui , se necessario;)
Nessuno ti suggerisce di comporre?
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
Su emulazioni di Windows come Git Bash ciò potrebbe non riuscire perché Windows non è sensibile al maiuscolo / minuscolo. Per quelli, aggiungi un passaggio che prima deve essere il file a un altro nome, come " $ old.tmp " ;, quindi a $ new.
Funziona bene anche su macOS:
ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh
Non ho provato gli script più elaborati menzionati qui, ma nessuna delle versioni a riga di comando singola ha funzionato per me sul mio Synology NAS. rename
non è disponibile e molte delle varianti di find
falliscono perché sembra aderire al vecchio nome del percorso già rinominato (ad es. se trova ./FOO
seguito da ./FOO/BAR
, rinominando ./FOO
in ./foo
continuerà comunque a elencare < code> ./ FOO / BAR anche se quel percorso non è più valido). Il comando sopra ha funzionato per me senza problemi.
Quello che segue è una spiegazione di ogni parte del comando:
find . -depth -name '*[A-Z]*'
Questo troverà qualsiasi file dalla directory corrente (cambia .
in qualunque directory tu voglia elaborare), usando una ricerca approfondita (es., elencherà ./foo / bar
prima di ./foo
), ma solo per i file che contengono un carattere maiuscolo. Il filtro -name
si applica solo al nome del file di base, non al percorso completo. Quindi questo elencherà ./FOO/BAR
ma non ./FOO/bar
. Questo va bene, poiché non vogliamo rinominare ./FOO/bar
. Vogliamo rinominare ./FOO
, ma quello è elencato più avanti (ecco perché -depth
è importante).
Questo comando in sé è particolarmente utile per trovare i file che si desidera rinominare in primo luogo. Utilizzare questo dopo il comando di ridenominazione completo per cercare file che non sono stati ancora sostituiti a causa di collisioni o errori di nomi di file.
sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'
Questa parte legge i file generati da find
e li formatta in un comando mv
usando un'espressione regolare. L'opzione -n
impedisce a sed
di stampare l'input e il comando p
nel regex cerca e sostituisci restituisce il testo sostituito.
La stessa regex è composta da due acquisizioni: la parte fino all'ultimo / (che è la directory del file) e il nome file stesso. La directory viene lasciata intatta, ma il nome file viene trasformato in minuscolo. Quindi, se trova
genera ./FOO/BAR
, diventerà mv -n -v -T ./FOO/BAR ./FOO/bar
. L'opzione -n
di mv
assicura che i file minuscoli esistenti non vengano sovrascritti. L'opzione -v
fa in modo che mv
emetta tutte le modifiche apportate (o non apportate - se ./FOO/bar
esiste già, genera qualcosa come ./FOO/BAR - > ./FOO/BAR
, notando che non sono state apportate modifiche). Il -T
è molto importante qui: tratta il file di destinazione come una directory. Ciò assicurerà che ./FOO/BAR
non venga spostato in ./FOO/bar
se la directory esiste.
Utilizzalo insieme a find
per generare un elenco di comandi che verranno eseguiti (utile per verificare cosa verrà fatto senza farlo effettivamente)
sh
Questo piuttosto autoesplicativo. Instrada tutti i comandi mv
generati all'interprete della shell. Puoi sostituirlo con bash
o con qualsiasi shell di tuo gradimento.