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

61

Ho molti file con estensione .abc e voglio cambiarli in .edefg
Come farlo dalla riga di comando?

Ho una cartella radice con molte sottocartelle, quindi la soluzione dovrebbe funzionare in modo ricorsivo.

    
posta tommyk 19.04.2011 - 14:02

8 risposte

75

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

find /the/path -depth -name "*.abc" -exec sh -c 'mv "" "${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 comando (perl) rename in Ubuntu può rinominare i file usando la sintassi perl regular expression, 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 link

    
risposta data geirha 19.04.2011 - 20:51
37

Questo 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, usa questo:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
    
risposta data Chakra 19.04.2011 - 14:23
11

Un problema con i nomi ricorsivi è che qualunque metodo tu usi per localizzare i file, passa l'intero percorso a rename , non solo al nome del file. Ciò rende difficile la definizione di nomi complessi nelle cartelle nidificate.

Uso l'azione find di -execdir per risolvere questo problema. Se si utilizza -execdir anziché -exec , il comando specificato viene eseguito dalla sottodirectory contenente il file corrispondente. Quindi, anziché passare l'intero percorso a rename , passa solo ./filename . Ciò rende molto più facile scrivere la regex.

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

In dettaglio:

  • -type f significa solo cercare file, non directory
  • -name '*.abc' significa significa solo nomi di corrispondenze che terminano in .abc
  • I backslash dopo -type e -name sono i caratteri di continuazione della riga bash. Li uso per rendere questo esempio più leggibile, ma in pratica non sono necessari.
  • Tuttavia, è richiesta la barra rovesciata alla fine della riga -execdir . È lì per esacpe il punto e virgola, che termina il comando eseguito da -execdir . Fun!

Spiegazione della regex:

  • s/ inizio della regex
  • \.\/ corrisponde al primo ./ che passa -execdir. Usa \ per sfuggire al. e / metacaratteri
  • (.+) corrisponde al nome file. Le parentesi catturano la corrispondenza per un uso successivo
  • \.abc sfugge al punto, corrisponde a abc
  • $ ancora la corrispondenza alla fine della stringa

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

  • version1_ aggiungi questo testo a ogni nome di file

  • fa riferimento al nome file esistente, perché lo abbiamo catturato con parentesi. Se utilizzi più set di parentesi nella parte "match", puoi fare riferimento a loro qui usando $ 2, $ 3, ecc.
  • .abc il nuovo nome del file terminerà in .abc. Non è necessario sfuggire al punto metacarattere qui nella sezione "Sostituisci"
  • / alla 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: l'opzione -n ​​di rename è utile. Effettua un ciclo di prova e ti mostra quali nomi cambierà, ma non apporta alcuna modifica.

    
risposta data Christian Long 06.10.2012 - 00:48
5

Un altro modo portatile:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "" "$(dirname "")/$(basename "" .abc).edefg"' _ '{}' \;
    
risposta data aNeutrino 03.02.2013 - 17:36
0
# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done

Vedi anche la voce sul perché non dovresti analizzare ls .

Modifica: se devi usare basename, la tua sintassi sarebbe:

for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done

link

    
risposta data ashanrupasinghe 22.08.2016 - 14:12
0

Questo è quello che ho fatto e ho funzionato bene proprio come volevo. Ho usato il comando mv . Ho avuto più% file% di file e volevo rinominarli tutti in .3gp

Ecco un breve oneliner per questo:

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

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

    
risposta data Rexford 13.03.2016 - 22:58
0

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

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done
    
risposta data Peaceful 27.09.2017 - 05:39
0

Vorrei utilizzare il comando mmv dal pacchetto con lo stesso nome :

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

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

mmv '*.abc' '#1.edefg'
    
risposta data MvG 20.05.2018 - 23:13

Leggi altre domande sui tag