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.

  1. 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.

  2. Vorrei considerare i caratteri dalla A alla Z e trasformarli in A-Z, tutto il resto richiede solo problemi (almeno con il codice sorgente).

  3. 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.

È stato utile?

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:

  1. 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 e abcdef ? Lo script di rinomina dovrebbe interrompersi o semplicemente avvisare e continuare?

  2. 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?

  3. 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

Come convertire una stringa in minuscolo in bash?

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

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top