Come posso modificare l'estensione di più file in modo ricorsivo dalla riga di comando?


Risposte:


85

Un modo portatile (che funzionerà su qualsiasi sistema conforme a POSIX):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

In bash4, puoi usare globstar per ottenere globi ricorsivi (**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

Il renamecomando (perl) in Ubuntu può rinominare i file usando la sintassi delle espressioni regolari perl, che puoi combinare con globstar o find:

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Vedi anche http://mywiki.wooledge.org/BashFAQ/030


il primo aggiunge semplicemente la nuova estensione a quella precedente, non sostituisce ...
user2757729

3
@ user2757729, lo sostituisce. "${1%.abc}"si espande al valore di $1tranne senza .abcalla fine. Vedi faq 100 per ulteriori informazioni sulle manipolazioni delle stringhe di shell.
geirha,

Ho eseguito questo su una struttura di file di ~ 70k file ... almeno sulla mia shell sicuramente non lo ha sostituito.
user2757729

1
@ user2757729 Probabilmente hai lasciato "abc" a${1%.abc}...
kvnn

1
Nel caso in cui qualcun altro si stia chiedendo cosa sta facendo il carattere di sottolineatura nel primo comando, è necessario perché con sh -c il primo argomento è assegnato a $ 0 e tutti gli argomenti rimanenti sono assegnati ai parametri posizionali . Quindi _è un argomento fittizio e '{}' è il primo argomento posizionale a sh -c.
ciaobenallan,

48

Ciò eseguirà l'attività richiesta se tutti i file si trovano nella stessa cartella

rename 's/.abc$/.edefg/' *.abc

Per rinominare i file in modo ricorsivo, utilizzare questo:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

8
O rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abcin una versione moderna di Bash.
Adam Byrtek,

Grazie mille Adam per avermi dato un consiglio su come usare ricorsivamente * .abc nelle cartelle!
Rafał Cieślak,

Ottimo consiglio, grazie! Dove posso trovare più documentazione sul piccolo pezzo di sintassi di regex? Come quello che è sall'inizio e quali altre opzioni posso usare lì dentro. Grazie !
Anto

@AdamByrtek Ho appena fallito con il tuo suggerimento su Utopic. ('nessun file del genere' o qualcosa del genere.)
Jonathan Y.

14

Un problema con le rinominazioni ricorsive è che qualunque metodo usi per localizzare i file, passa l'intero percorso rename, non solo il nome del file. Ciò rende difficile eseguire rinominazioni complesse nelle cartelle nidificate.

Uso findl’ -execdirazione per risolvere questo problema. Se si utilizza -execdirinvece di -exec, il comando specificato viene eseguito dalla sottodirectory contenente il file corrispondente. Quindi, invece di passare l'intero percorso rename, passa solo ./filename. Ciò rende molto più semplice scrivere la regex.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

In dettaglio:

  • -type f significa solo cercare file, non directory
  • -name '*.abc' indica solo i nomi dei file che terminano in .abc
  • '{}'è il segnaposto che contrassegna il punto in cui -execdirverrà inserito il percorso trovato. Le virgolette singole sono necessarie per consentire la gestione dei nomi dei file con spazi e caratteri shell.
  • Le barre rovesciate dopo -typee -namesono il carattere di continuazione della riga bash. Li uso per rendere questo esempio più leggibile, ma non sono necessari se si inserisce il comando su una riga.
  • Tuttavia, è richiesta la barra rovesciata alla fine della -execdirriga. È lì per sfuggire al punto e virgola, che termina il comando eseguito da -execdir. Divertimento!

Spiegazione della regex:

  • s/ inizio della regex
  • \.\/corrisponde al comando ./ che passa -execdir. Usa \ per sfuggire a. e / metacaratteri (nota: questa parte varia in base alla versione di find. Vedi commento dell'utente @apollo)
  • (.+)corrisponde al nome del file. Le parentesi catturano la corrispondenza per un uso successivo
  • \.abc sfuggire al punto, abbinare l'abc
  • $ ancorare la corrispondenza alla fine della stringa

  • / segna la fine della parte "match" del regex e l'inizio della parte "sostituisci"

  • version1_ aggiungi questo testo ad ogni nome di file

  • $1fa riferimento al nome file esistente, perché l'abbiamo catturato tra parentesi. Se usi più set di parentesi nella parte "match", puoi fare riferimento a questi qui usando $ 2, $ 3, ecc.
  • .abcil nuovo nome del file terminerà con .abc. Non è necessario sfuggire al punto metacarattere nella sezione "Sostituisci"
  • / fine della regex

Prima

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

Dopo

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

Suggerimento: renamel'opzione 's -n è utile. Fa una corsa a secco e ti mostra quali nomi cambierà, ma non apporta alcuna modifica.


Grazie per la spiegazione dei dettagli, ma stranamente, quando provo questo, --execdirnon è passato in testa ./quindi la \.\/parte nella regex può essere omessa. Che sia perché sono su OSX non su Ubuntu
Apollo,

5

Un altro modo portatile:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

4
Benvenuti in Ask Ubuntu! Sebbene questa sia una risposta preziosa, ti consiglio di espanderla (modificando) per spiegare come e perché funziona quel comando.
Eliah Kagan,


0

Questo è quello che ho fatto e lavorato abbastanza come volevo. Ho usato il mvcomando. Avevo più .3gpfile e volevo rinominarli tutti.mp4

Ecco un breve suggerimento per questo:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Che semplicemente scansiona la directory corrente, raccoglie tutti i file .3gp, quindi rinomina (usando mv) in ren-name_of_file.mp4


Ciò verrà rinominato file.3gpin file.3gp.mp4, che potrebbe non essere quello che la maggior parte delle persone desidera.
muru,

@muru ma il principio esiste ancora. È sufficiente selezionare qualsiasi estensione quindi specificare l'interno di destinazione per farli rinominate. Il .3gpo .mp4qui erano solo a scopo illustrativo.
KhoPhi,

La sintassi è preferibile, one-liner e facile da capire. Tuttavia, vorrei basename "$i" .mp4rimuovere l'estensione precedente invece di "ren- $ i.mp4".
TaoPR,

Vero. Buon punto.
KhoPhi,

0

Ho trovato un modo semplice per raggiungere questo obiettivo. Per modificare le estensioni di molti file da jpg a pdf, utilizzare:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done

0

Userei il comando di MMV dal pacchetto con lo stesso nome :

mmv ';*.abc' '#1#2.edefg'

Le ;partite corrispondono a zero o più */e corrispondono a #1nella sostituzione. Il *corrisponde a #2. La versione non ricorsiva sarebbe

mmv '*.abc' '#1.edefg'

0

Rinominare file e directory con find -execdir | rename

Se hai intenzione di rinominare sia i file che le directory non semplicemente con un suffisso, allora questo è un buon modello:

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

La fantastica -execdiropzione fa un cdnella directory prima di eseguire il renamecomando, a differenza -exec.

-depth assicurarsi che la ridenominazione avvenga prima sui figli, e poi sui genitori, per prevenire potenziali problemi con le directory dei genitori mancanti.

-execdir è necessario perché la ridenominazione non viene riprodotta correttamente con percorsi di input senza nome di base, ad esempio i seguenti errori:

rename 's/findme/replaceme/g' acc/acc

L' PATHhacking è necessario perché -execdirha uno svantaggio molto fastidioso: findè estremamente supponente e si rifiuta di fare qualsiasi cosa -execdirse ci sono percorsi relativi nella PATHvariabile di ambiente, ad esempio ./node_modules/.bin, non riuscendo con:

find: il percorso relativo './node_modules/.bin' è incluso nella variabile d'ambiente PATH, che non è sicura in combinazione con l'azione -execdir di find. Rimuovi quella voce da $ PATH

Vedi anche: Perché l'uso dell'azione '-execdir' non è sicuro per la directory che si trova nel PERCORSO?

-execdirè un'estensione di ricerca GNU a POSIX . renameè basato su Perl e proviene dal renamepacchetto. Testato su Ubuntu 18.10.

Rinomina soluzione alternativa lookahead

Se i tuoi percorsi di input non provengono findo se hai abbastanza del fastidio relativo del percorso, possiamo usare alcuni lookahead Perl per rinominare in modo sicuro le directory come in:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

Non ho trovato un analogo conveniente per -execdircon xargs: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686

Il sort -rè tenuto a garantire che i file vengono dopo rispettive directory, dal momento che i percorsi più lunghi vengono dopo quelle più corte con lo stesso prefisso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.