Rinominare un gran numero di file immagine con bash


16

Ho bisogno di rinominare ca. 70.000 file. Ad esempio: da sb_606_HBO_DPM_0089000a sb_606_dpm_0089000ecc.

L'intervallo numerico va da 0089000a 0163022. È solo la prima parte del nome che deve cambiare. tutti i file sono in una singola directory e sono numerati in sequenza (una sequenza di immagini). I numeri devono rimanere invariati.

Quando provo questo a Bash, mi grida che "La lista degli argomenti è troppo lunga".

Modificare:

Per prima cosa ho provato a rinominare un singolo file con mv:

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

Poi ho provato a rinominare un intervallo (ho imparato qui la scorsa settimana come spostare un carico di file, quindi ho pensato che la stessa sintassi potesse funzionare per rinominare i file ...). Io penso che ho provato quanto segue (o qualcosa di simile):

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

4
Per i revisori : non credo sia un duplicato; la maggior parte delle risposte della CLI sull'altra domanda non funzionerà qui a causa dell'elevato numero di file che si scontrano con il ARG_MAXlimite della shell . Poiché questa domanda richiede esplicitamente una soluzione da riga di comando, anche le soluzioni GUI (possibilmente uguali) come nell'altra domanda non corrispondono.
dessert

1
Non penso che questo sia un duplicato perché è ok avere più di una domanda sulla ridenominazione dei file. Per favore, non chiudiamo domande specifiche contro risorse generiche che in realtà non rispondono ...
Zanna,

1
@rich Se riesci a modificare esplicitamente quale comando hai provato, sarebbe più chiaro che questo non è un duplicato. (Questo ci mostra che sei a conoscenza di questo approccio.) Saluti.
Sparhawk,

2
ricca, la tua domanda non è un inganno, perché è una domanda specifica. Non ti preoccupare. Ancora più importante, dopo che una domanda ha ricevuto una serie di risposte votate, la modifica non è probabilmente una buona idea perché le modifiche potrebbero rendere le risposte esistenti meno valide. Ora sento che la mia risposta dovrebbe spiegare perché mv {1..2} {3..4}non funziona, il che è un problema completamente diverso da ARG_MAX... Tutti gli altri che hanno risposto probabilmente sentiranno lo stesso! Quindi, dal mio punto di vista, vorrei che tu ripristinassi la tua ultima modifica e, se vuoi, fai una domanda completamente nuova mvsull'ing con intervalli
Zanna

1
@Sparhawk l'OP ha scritto abbastanza chiaramente, dalla prima versione della domanda, che il problema è l' argument list too longerrore. Non è necessario chiarire ulteriormente, questo chiaramente non è un inganno poiché abbiamo bisogno di una soluzione alternativa per gestire ARG_MAX e le risposte nel duplicato proposto non lo fanno.
terdon,

Risposte:


25

Un modo è usare findcon -exece l' +opzione. Questo crea un elenco di argomenti, ma suddivide l'elenco in tutte le chiamate necessarie per operare su tutti i file senza superare l'elenco massimo di argomenti. È adatto quando tutti gli argomenti saranno trattati allo stesso modo. Questo è il caso con rename, sebbene non con mv.

Potrebbe essere necessario installare Perl rename:

sudo apt install rename

Quindi è possibile utilizzare, ad esempio:

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

Rimuovi -ndopo il test, per rinominare effettivamente i file.


11

Ho intenzione di suggerire tre alternative. Ognuno è un semplice comando a riga singola, ma fornirò varianti per casi più complicati, principalmente nel caso in cui i file da elaborare vengano mescolati con altri file nella stessa direttrice.

mmv

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

mmv '*HBO_DPM*' '#1dpm#2'

Si noti che gli argomenti vengono passati come stringhe, quindi l'espansione glob non avviene nella shell. Il comando riceve esattamente due argomenti, quindi trova i file corrispondenti internamente, senza limiti stretti sul numero di file. Si noti inoltre che il comando sopra presuppone che tutti i file che corrispondono al primo glob devono essere rinominati. Naturalmente sei libero di essere più specifico:

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

Se hai file al di fuori dell'intervallo di numeri richiesto nella stessa directory, potresti essere meglio con il ciclo sui numeri indicati più in basso in questa risposta. Tuttavia, è possibile utilizzare anche una sequenza di invocazioni mmv con schemi adeguati:

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

scorrere i numeri

Se vuoi evitare di installare qualcosa o devi selezionare per intervallo di numeri evitando corrispondenze al di fuori di questo intervallo e sei pronto ad aspettare 74,023 invocazioni di comandi, puoi usare un semplice ciclo bash:

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

Questo funziona particolarmente bene qui poiché non ci sono lacune nella sequenza. Altrimenti potresti voler verificare se il file sorgente esiste realmente.

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

Si noti che in contrasto con for ((i=89000; i<=163022; ++i))l'espansione del controvento gestisce gli zeri iniziali da quando alcuni Bash rilasciano un paio di anni fa. In realtà una modifica che ho richiesto, quindi sono felice di vedere casi d'uso per questo.

Ulteriori letture: Brace Expansion nelle pagine informative di Bash, in particolare la parte su {x..y[..incr]}.

loop su file

Un'altra opzione sarebbe quella di eseguire il loop su un glob adatto, anziché semplicemente il loop sull'intero intervallo in questione. Qualcosa come questo:

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

Anche questa è una mvchiamata per file. E ancora il ciclo è su un lungo elenco di elementi, ma l'intero elenco non viene passato come argomento a un sottoprocesso, ma gestito internamente da bash, quindi il limite non ti causerà problemi.

Ulteriori letture: Espansione dei parametri della shell nelle pagine informative di Bash, che documentano ${parameter/pattern/string}tra l'altro.

Se si desidera limitare l'intervallo di numeri a quello fornito, è possibile aggiungere un controllo per questo:

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

Qui ${i##pattern}rimuove la corrispondenza del prefisso più lungo patternda $i. Quel prefisso più lungo viene definito come qualsiasi cosa, quindi un carattere di sottolineatura, quindi zero o più zeri. Quest'ultimo è scritto come *(0)un modello glob esteso che dipende extglobdall'opzione impostata. La rimozione degli zeri iniziali è importante per trattare il numero come base 10 e non base 8. L' +([0-9])argomento nel ciclo è un altro glob esteso, corrispondente a una o più cifre, nel caso in cui ci siano file che iniziano lo stesso ma non finiscono in un numero.


Grazie! Funzionava come un sogno: per me in {0089000..0163022}; mv sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; fatto - ho dovuto aggiungere l'estensione del nome per farlo funzionare, ma ha fatto esattamente quello che volevo e ho anche capito la sintassi. Grazie @MvG
ricco

@rich: felice di poterti aiutare, anche tu, e spero, anche i futuri visitatori. Non dimenticare di accettare la risposta più utile. È sempre possibile modificare quel segno di spunta in futuro se si presenta qualcosa di meglio.
MvG

10

Un modo per aggirare il ARG_MAXlimite è usare l'integrato della shell bash printf:

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

Ex.

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

ma

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

7
find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \;
./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000

findnella directory corrente .per tutti i file -type fe rinominare il file trovato $1sostituendolo HBO_DPMcon dmp uno per uno-exec ... \;

sostituire echocon mvper eseguire la ridenominazione.


6

Potresti scrivere un piccolo script Python, qualcosa del tipo:

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

Salvalo come file di testo come rename.pynella cartella in cui si trovano i file, quindi con il terminale in quella cartella vai:

python rename.py

6

Puoi farlo file per file (potrebbe richiedere del tempo) con

sudo apt install util-linux  # if you don't have it already
for i in *; do rename.ul HBO_DPM dpm "$i"; done

Come il Perl renameutilizzato in altre risposte, rename.ulha anche un'opzione -no --no-actper i test.


Ho modificato il tuo commento sulla risposta di Zanna, per favore modifica la risposta di Zanna o lascia un commento.
fosslinux,

@ubashu che non era un commento sulla mia risposta - si riferiva alla -nbandiera che ho usato per i test e suggerendo che può essere utilizzato rename.ulanche in.
Zanna,

3

Vedo che nessuno ha invitato il mio migliore amico sedalla festa :). Il seguente forciclo raggiungerà il tuo obiettivo:

for i in sb_606_HBO_DPM*; do
  mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')";
done

Esistono molti strumenti per un simile lavoro, seleziona quello più comprensibile per te. Questo è semplice e facilmente modificabile per soddisfare questo o altri scopi ...


Concesso, non molto pertinente in questo caso specifico, ma ciò fallirà se uno dei nomi dei file contiene nuove righe. Lo dico poiché la maggior parte (tutte?) Delle altre risposte sono robuste e possono gestire nomi di file arbitrari o funzionare solo sullo schema di denominazione dei file dell'OP.
terdon,

... newline, spazi, caratteri jolly, ... alcuni dei quali possono essere evitati citando $ila sostituzione del comando, ma nessun modo semplice per gestire una nuova riga finale nel nome del file.
muru,

3

Dal momento che stiamo offrendo opzioni, ecco un approccio Perl. cdnella directory di destinazione ed eseguire:

perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}'

Spiegazione

  • perl -e: esegue lo script fornito da -e.
  • foreach(glob){}: esegui qualunque cosa sia presente { }su ogni risultato del glob.
  • glob("sb_*"): restituisce un elenco di tutti i file e le directory nella directory corrente i cui nomi corrispondono alla shell glob sb*.
  • rename $_, s/_HBO_DPM_/_dpm_/r: perl magic. $_è una variabile speciale che contiene ogni elemento su cui stiamo ripetendo (nel foreach). Quindi qui, verrà trovato ogni file. s/_HBO_DPM_/_dpm_/sostituisce la prima occorrenza di _HBO_DPM_con _dpm_. Funziona $_di default, quindi verrà eseguito su ciascun nome di file. Il /rmezzo "applica questa sostituzione a una copia della stringa di destinazione (il nome del file) e restituisce la stringa modificata. renameFa quello che ti aspetteresti: rinomina i file. Quindi l'intera cosa rinominerà il nome del file corrente ( $_) su se stesso con _HBO_DPM_sostituito da _dpm_.

Potresti scrivere la stessa cosa di uno script espanso (e più leggibile):

#! /usr/bin/env perl
use strict;
use warnings;

foreach my $fileName (glob("sb_*")){
  ## Copy the name to a new variable
  my $newName = $fileName;
  ## change the copy. $newName is now the changed version
  $newName =~ s/_HBO_DPM_/_dpm_/;
  ## rename
  rename $fileName, $newName;
}

1

A seconda del tipo di ridenominazione che stai immaginando, l'utilizzo di vidir con la modifica di più righe potrebbe essere soddisfacente.
Nel tuo caso particolare puoi selezionare tutte le righe nel tuo editor di testo e rimuovere la parte _ " HBO" dei nomi dei file in pochi tasti.


Sì, vi ha gloable trovare e sostituire.
Jasen,

2
Potete per favore estendere la vostra risposta e dare un esempio di come raggiungere l'obiettivo del PO vidir?
dessert
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.