Rinomina restituisce "bareword non consentito" quando si tenta di minuscole parti di più nomi di file


12

Ho due file in una cartella sul mio Ubuntu 16.04:

a1.dat
b1.DAT

Voglio rinominare b1.DATin b1.datmodo da avere i seguenti file come risultato nella cartella:

a1.dat
b1.dat

Ho provato (senza successo):

$ rename *.DAT *.dat
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

e

$ find . -iname "*.DAT" -exec rename DAT dat '{}' \;
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

La ricerca di questo non ha portato a una soluzione significativa ...


Potresti anche voler conoscere mmv
PlasmaHH il

Risposte:


14

Questo errore sembra provenire da Perl rename. Devi usare il preventivo, ma devi solo specificare la parte che vuoi cambiare, cercare e sostituire lo stile. La sintassi è così:

rename -n 's/\.DAT/\.dat/' *

Rimuovere -ndopo il test per rinominare effettivamente i file.

Per includere file nascosti, modificare le impostazioni glob prima di eseguire il comando:

shopt -s dotglob

Se si desidera rinominare i file in modo ricorsivo, è possibile utilizzare

shopt -s globstar
rename -n 's/\.DAT/\.dat/' **

o se ci sono molti percorsi all'interno o al di sotto della directory corrente che non terminano .DAT, è meglio specificare quei percorsi nel secondo comando:

rename -n 's/\.DAT/\.dat/' **/*.DAT

Questo sarà più veloce se i tuoi file hanno vari nomi diversi che non finiscono .DAT. [1]

Per disattivare queste impostazioni è possibile utilizzare shopt -u, ad esempio shopt -u globstar, ma sono disattivate per impostazione predefinita e saranno disattivate quando si apre una nuova shell.

Se ciò si traduce in un elenco di argomenti eccessivamente lungo, è possibile utilizzare, ad esempio find:

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} \;

o meglio

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} +

L'uso find ... -execcon +è più veloce dell'uso \;perché costruisce un elenco di argomenti dai file trovati. Inizialmente pensavo che non sarebbe stato possibile usarlo, perché hai detto che stavi avendo un argument list too longproblema, ma ora so che l'elenco verrà anche abilmente suddiviso in più invocazioni del comando in base alle necessità per evitare quel problema [2] .

Poiché renameelaborerà ogni nome di file allo stesso modo, non importa quanto sia lungo l'elenco degli argomenti in quanto può essere suddiviso in modo sicuro tra più invocazioni. Se il comando con cui si sta utilizzando -execnon accetta più argomenti o richiede che i suoi argomenti siano in un ordine particolare o per qualsiasi altra ragione la divisione dell'elenco degli argomenti provocherà qualcosa di indesiderato, è possibile utilizzare \;, il che fa invocare il comando una volta per ogni file trovato (se l'elenco degli argomenti era troppo lungo per altri metodi, ciò richiederà molto tempo!).


Mille grazie a Eliah Kagan per i suggerimenti molto utili per migliorare questa risposta:

[1] Specifica dei nomi dei file durante il globbing .
[2] find ... -execcon +divide l'elenco degli argomenti .


Grazie. Come farlo in modo ricorsivo (per tutti i file in tutte le sottocartelle)?
Samo,

@Samo vedi la mia modifica :)
Zanna

Non funziona: $ rename 's / \. DAT / \. Dat /' ** bash: / usr / bin / rename: elenco degli argomenti troppo lungo
Samo

1
@Samo è necessario rimuovere il -nda rinominare dopo il test - è solo per mostrare cosa sarà cambiato
Zanna

1
Su Centos e Arch, la ridenominazione non presenta questo comportamento. Sono arrivato qui usando Debian su WSL. Questa sintassi ha funzionato.
xtian

6

Tu puoi fare:

rename -n 's/DAT$/\L$&/' *.DAT

Rilascia -nper consentire la ridenominazione effettiva.

  • il modello glob *.DATcorrisponde a tutti i file che finiscono nella .DATdirectory corrente

  • nella renamesostituzione, DAT$partite DATalla fine

  • \L$&rende l'intera partita minuscola; $&si riferisce all'intera partita

Se vuoi fare solo per b1.DAT:

rename -n 's/DAT$/\L$&/' b1.DAT

Esempio:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)

4

Altre risposte hanno affrontato uno dei due aspetti principali di questa domanda: quello di come eseguire con successo l'operazione di ridenominazione necessaria. Lo scopo di questa risposta è spiegare perché i tuoi comandi non hanno funzionato, incluso il significato di quello strano messaggio di errore "password non consentita" nel contesto del renamecomando.

La prima sezione di questa risposta riguarda la relazione tra renamee Perl e il modo in cui renameutilizza il primo argomento della riga di comando che viene passato, ovvero l'argomento codice. La seconda sezione riguarda il modo in cui la shell esegue espansioni - in particolare globbing - per costruire un elenco di argomenti. La terza sezione riguarda ciò che sta accadendo nel codice Perl che genera errori "Bareword non consentito". Infine, la quarta sezione è un riepilogo di tutti i passaggi che avvengono tra l'immissione del comando e il recupero dell'errore.

1. Quando renameti danno strani messaggi di errore, aggiungi "Perl" alla tua ricerca.

In Debian e Ubuntu, il renamecomando è uno script Perl che esegue la ridenominazione dei file. Nelle versioni precedenti, tra cui 14.04 LTS, che è ancora supportato al momento della stesura di questo documento, si trattava di un collegamento simbolico che puntava ( indirettamente ) al prenamecomando. Nelle versioni più recenti, punta invece al file-renamecomando più recente . Quei due comandi di rinomina Perl funzionano principalmente allo stesso modo, e farò riferimento a entrambi come renameper il resto di questa risposta.

Quando usi il renamecomando, non stai solo eseguendo il codice Perl scritto da qualcun altro. Stai anche scrivendo il tuo codice Perl e stai dicendo renamedi eseguirlo. Questo perché il primo argomento della riga di comando che passi al renamecomando, diverso dagli argomenti di opzione come -n, è costituito dal codice Perl effettivo . Il renamecomando utilizza questo codice per operare su ciascuno dei nomi di percorso che viene passato come successivo argomento della riga di comando. (Se non passi alcun argomento sul nome percorso, renamelegge invece i nomi percorso dall'input standard , uno per riga.)

Il codice viene eseguito all'interno di un ciclo , che viene ripetuto una volta per percorso. Nella parte superiore di ogni iterazione del ciclo, prima dell'esecuzione del codice, alla $_variabile speciale viene assegnato il nome percorso attualmente in fase di elaborazione. Se il tuo codice fa sì che il valore di $_venga cambiato in qualcos'altro, quel file viene rinominato con il nuovo nome.

Molte espressioni in Perl operano implicitamente sulla $_variabile quando non hanno altre espressioni da usare come operando . Ad esempio, l'espressione sostituzione $str =~ s/foo/bar/cambia la prima occorrenza foonella stringa detenuto dalla $str variabile a bar, o foglie inalterata se non contiene foo. Se scrivi semplicemente s/foo/bar/senza usare esplicitamente l' =~operatore , allora funziona $_. Questo per dire che s/foo/bar/è l'abbreviazione di $_ =~ s/foo/bar/.

E 'comune per passare un'espressione di come l'argomento di codice (ad esempio, il primo argomento della riga di comando), ma non è necessario. Puoi dargli qualsiasi codice Perl che desideri venga eseguito all'interno del ciclo, per esaminare ogni valore e (condizionatamente) modificarlo.s///rename$_

Ciò ha molte conseguenze interessanti e utili , ma la stragrande maggioranza di esse va ben oltre lo scopo di questa domanda e risposta. Il motivo principale per cui lo mostro qui - in effetti, il motivo principale per cui ho deciso di pubblicare questa risposta - è quello di sottolineare che, poiché il primo argomento renameè il codice Perl, ogni volta che ricevi un messaggio di errore strano e tu hai difficoltà a trovare informazioni a riguardo cercando, puoi aggiungere "Perl" alla stringa di ricerca (o persino sostituire "rinominare" con "Perl", a volte) e spesso troverai una risposta.

2. Con rename *.DAT *.dat, il renamecomando non ha mai visto *.DAT!

Un comando come in rename s/foo/bar/ *.txtgenere non passa *.txtcome argomento della riga di comando al renameprogramma e non lo si desidera, a meno che non si disponga di un file il cui nome è letteralmente *.txt, cosa che si spera non sia possibile.

renamenon interpreta i modelli glob piace *.txt, *.DAT, *.dat, x*, *y, o *quando passa ad esso come argomenti del percorso. Invece, la tua shell esegue l'espansione del nome del percorso su di essi (che è anche chiamato espansione del nome file e anche chiamato globbing). Ciò accade prima che renamevenga eseguita l' utilità. La shell espande i globs in percorsi potenzialmente multipli e li passa tutti, come argomenti della riga di comando separati, a rename. In Ubuntu, la tua shell interattiva è Bash , a meno che tu non l'abbia modificata, motivo per cui ho collegato il manuale di riferimento di Bash sopra.

Esiste una situazione in cui un modello glob può essere passato come singolo argomento della riga di comando non espanso a rename: quando non corrisponde a nessun file. Diverse shell mostrano un comportamento predefinito diverso in questa situazione, ma il comportamento predefinito di Bash è semplicemente quello di passare letteralmente il globo. Tuttavia, raramente lo desideri! Se lo volevi, dovresti assicurarti che il modello non sia espanso, citandolo . Questo vale per passare argomenti a qualsiasi comando, non solo a rename.

La citazione non è solo per globbing (espansione del nome del file), perché ci sono altre espansioni che la tua shell esegue sul testo non quotato e, per alcuni di essi ma non altri , anche sul testo racchiuso tra " "virgolette. In generale, ogni volta che si desidera passare un argomento che contiene caratteri che possono essere trattati in modo speciale dalla shell, inclusi gli spazi, è necessario citarlo, preferibilmente tra ' 'virgolette .

Il codice Perl s/foo/bar/non contiene nulla trattato in modo speciale dalla shell, ma sarebbe stata una buona idea anche per me averlo citato e scritto 's/foo/bar/'. (In realtà, l'unico motivo che ho fatto non era che sarebbe fonte di confusione per alcuni lettori, come io non avevo ancora parlato citando.) Il motivo per cui dico questo sarebbe stato buono è perché è molto comune che il codice Perl non contiene tali personaggi, e se dovessi cambiare quel codice, potrei non ricordare di verificare se fosse necessario un preventivo. Al contrario, se si desidera che il guscio di espandersi in un glob, deve non essere citato.

3. Cosa intende l'interprete Perl con "password non consentita"

I messaggi di errore che hai mostrato nella tua domanda rivelano che, quando hai eseguito rename *.DAT *.dat, la tua shell si è espansa *.DATin un elenco di uno o più nomi di file e che il primo di quei nomi di file era b1.DAT. Tutti gli argomenti successivi, sia quelli espansi *.DATche quelli espansi, sono *.datarrivati ​​dopo quell'argomento, quindi sarebbero stati interpretati come nomi di percorso.

Perché ciò che è stato effettivamente eseguito è stato qualcosa di simile rename b1.DAT ...e perché renameconsidera il suo primo argomento non opzionale come codice Perl, la domanda diventa: perché b1.DATproduce questi errori "non consentiti" quando lo esegui come codice Perl?

Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

In una shell, citiamo le nostre stringhe per proteggerle da espansioni involontarie della shell che altrimenti le trasformerebbero automaticamente in altre stringhe (vedere la sezione sopra). Le shell sono linguaggi di programmazione per scopi speciali che funzionano in modo molto diverso dai linguaggi di uso generale (e la loro strana sintassi e semantica lo riflettono). Ma Perl è un linguaggio di programmazione generico e, come la maggior parte dei linguaggi di programmazione generici, lo scopo principale di citare in Perl non è proteggere le stringhe, ma menzionarle affatto. Questo è in realtà un modo in cui la maggior parte dei linguaggi di programmazione sono simili al linguaggio naturale. In inglese, e supponendo che tu abbia un cane, "il tuo cane" è una frase di due parole, mentre il tuo cane è un cane. Allo stesso modo, in Perl, '$foo'è una stringa, mentre $fooè qualcosa il cui nome è $foo.

Tuttavia, a differenza di quasi tutti gli altri linguaggi di programmazione general-purpose, Perl anche a volte interpretare il testo non quotate come citano una stringa - una stringa che è la "stessa", come, nel senso che si compone degli stessi personaggi in lo stesso ordine. Tenterà di interpretare il codice in questo modo solo se si tratta di una parola nuda (no $o altro sigillo, vedi sotto), e dopo non riuscirà a trovare nessun altro significato per dargli. Quindi lo prenderà come una stringa, a meno che tu non lo dica abilitando le restrizioni .

Le variabili in Perl in genere iniziano con un carattere di punteggiatura chiamato sigillo , che specifica il tipo ampio della variabile. Ad esempio, $significa scalare , @significa array e %significa hash . ( Ce ne sono altri. ) Non preoccuparti se lo trovi confuso (o noioso), perché lo sto solo sollevando per dire che quando un nome valido appare in un programma Perl ma non è preceduto da un sigillo, quel nome si dice che sia una parola nuda .

Le parole chiave servono a vari scopi, ma di solito indicano una funzione integrata o una subroutine definita dall'utente che è stata definita nel programma (o in un modulo utilizzato dal programma). Perl non ha funzioni integrate chiamate b1o DAT, quindi quando l'interprete Perl vede il codice b1.DAT, cerca di trattare b1e DATcome nomi di subroutine. Supponendo che non siano state definite tali subroutine, ciò fallisce. Quindi, a condizione che le restrizioni non siano state abilitate, le tratta come stringhe. Funzionerà, anche se la tua intenzione è reale o meno . L' .operatore di Perl concatena le stringhe , quindi b1.DATvaluta la stringab1DAT. Cioè, b1.DATè un brutto modo di scrivere qualcosa di simile 'b1' . 'DAT'o "b1" . "DAT".

Puoi provarlo tu stesso eseguendo il comando perl -E 'say b1.DAT', che passa il breve script say b1.DATPerl all'interprete Perl, che lo esegue, stampandolo b1DAT. (In questo comando, le ' 'citazioni dicono guscio di passare say b1.DATcome un unico argomento della riga di comando, altrimenti lo spazio causerebbe saye b1.DATviene analizzato come parole separate e perlli riceverebbe come argomenti separati. perlNon non vedere le citazioni stessi, come la shell li rimuove .)

Ma ora, prova a scrivere primause strict; nello script Perl say. Ora fallisce con lo stesso tipo di errore che hai ricevuto rename:

$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.

Ciò è accaduto perché use strict;vietava all'interprete Perl di trattare le parole d'ordine come stringhe. Per proibire questa particolare funzionalità, sarebbe davvero sufficiente abilitare solo la subsrestrizione. Questo comando produce gli stessi errori di cui sopra:

perl -E 'use strict "subs"; say b1.DAT'

Ma di solito i programmatori Perl scriveranno semplicemente use strict;, il che abilita la subslimitazione e altri due. use strict;è generalmente una pratica raccomandata. Quindi il renamecomando fa questo per il tuo codice. Ecco perché ricevi quel messaggio di errore.

4. In sintesi, ecco cosa è successo:

  1. La shell è passata b1.DATcome primo argomento della riga di comando, che ha funzionato renamecome codice Perl da eseguire in un ciclo per ogni argomento del percorso.
  2. Questo è stato preso per significare b1e DATcollegato con l' .operatore.
  3. b1e DATnon erano preceduti da sigilli, quindi venivano trattati come parole spoglie.
  4. Queste due parole nude sarebbero state prese come nomi di funzioni integrate o di subroutine definite dall'utente, ma nessuna di queste cose aveva uno di quei nomi.
  5. Se i "sottotitoli rigidi" non fossero stati abilitati, sarebbero stati trattati come le espressioni di stringa 'b1'e 'DAT'e concatenati. È lontano da ciò che intendevi, il che chiarisce come questa funzione spesso non sia utile.
  6. Ma "subs severe" è stata attivata, in quanto renameconsente a tutti le restrizioni ( vars, refs, e subs). Pertanto, hai ricevuto invece un errore.
  7. renamechiuso a causa di questo errore. Poiché questo tipo di errore si è verificato in anticipo, non è stato effettuato alcun tentativo di ridenominazione dei file, anche se non è stato superato -n. Questa è una buona cosa, che spesso protegge gli utenti da modifiche involontarie al nome del file e occasionalmente anche dall'effettiva perdita di dati.

Grazie a Zanna , che mi ha aiutato a risolvere alcune importanti carenze in una bozza più accattivante di questa risposta . Senza di lei, questa risposta avrebbe avuto molto meno senso e potrebbe non essere pubblicata affatto.

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.