Outras respostas abordaram um dos dois principais aspectos desta pergunta: como executar com êxito a operação de renomeação necessária. O objetivo desta resposta é explicar por que seus comandos não funcionaram, incluindo o significado dessa estranha mensagem de erro "palavra de barra não permitida" no contexto do rename
comando.
A primeira seção desta resposta é sobre o relacionamento entre rename
e Perl e como rename
usa o primeiro argumento da linha de comando que você passa, que é o seu argumento de código. A segunda seção é sobre como o shell executa expansões - especificamente globbing - para construir uma lista de argumentos. A terceira seção é sobre o que está acontecendo no código Perl que fornece erros "Bareword not allowed". Finalmente, a quarta seção é um resumo de todas as etapas que ocorrem entre inserir o comando e obter o erro.
1. Quando rename
receber mensagens de erro estranhas, adicione "Perl" à sua pesquisa.
No Debian e no Ubuntu, o rename
comando é um script Perl que executa a renomeação de arquivos. Em versões mais antigas - incluindo 14.04 LTS, que ainda é suportado até o momento -, era um link simbólico apontando ( indiretamente ) para o prename
comando. Nas versões mais recentes, ele aponta para o file-rename
comando mais recente . Esses dois comandos de renomeação de Perl funcionam da mesma maneira, e vou apenas me referir a ambos como rename
para o restante desta resposta.
Quando você usa o rename
comando, você não está apenas executando o código Perl que outra pessoa escreveu. Você também está escrevendo seu próprio código Perl e dizendo rename
para executá-lo. Isso ocorre porque o primeiro argumento de linha de comando que você passar para o rename
comando, com excepção argumentos opcionais como -n
, consiste de código Perl real . O rename
comando usa esse código para operar em cada um dos nomes de caminho que você passa como argumentos de linha de comando subsequentes. (Se você não passar nenhum argumento de nome de caminho, então rename
lê nomes de caminho da entrada padrão , um por linha.)
O código é executado dentro de um loop , que itera uma vez por nome de caminho. Na parte superior de cada iteração do loop, antes da execução do código, a $_
variável especial recebe o nome do caminho que está sendo processado no momento. Se seu código fizer com que o valor $_
seja alterado para outra coisa, esse arquivo será renomeado para ter o novo nome.
Muitas expressões em Perl operam implicitamente na $_
variável quando não recebem outra expressão para usar como operando . Por exemplo, a expressão de substituição $str =~ s/foo/bar/
altera a primeira ocorrência de foo
na cadeia mantida pela $str
variável para bar
ou a deixa inalterada se não contiver foo
. Se você acabou de escrever s/foo/bar/
sem usar explicitamente o =~
operador , ele continuará funcionando $_
. Isso quer dizer que s/foo/bar/
é a abreviação de $_ =~ s/foo/bar/
.
É comum passar uma s///
expressão de rename
como o argumento de código (ou seja, o primeiro argumento de linha de comando), mas você não tem que. Você pode fornecer qualquer código Perl que você deseja que seja executado dentro do loop, para examinar cada valor $_
e (condicionalmente) modificá-lo.
Isso tem muitas consequências interessantes e úteis , mas a grande maioria delas está muito além do escopo desta pergunta e resposta. A principal razão pela qual eu trouxe isso aqui - de fato, a principal razão pela qual decidi postar esta resposta - é enfatizar que, porque o primeiro argumento a rename
ser realmente o código Perl, sempre que você receber uma mensagem de erro estranha e você Se tiver problemas para encontrar informações sobre isso pesquisando, você pode adicionar "Perl" à string de pesquisa (ou até mesmo substituir "renomear" por "Perl", às vezes) e geralmente encontrará uma resposta.
2. Com rename *.DAT *.dat
, o rename
comando nunca viu *.DAT
!
Um comando como rename s/foo/bar/ *.txt
normalmente não passa *.txt
como um argumento de linha de comando para o rename
programa, e você não deseja, a menos que tenha um arquivo cujo nome seja literalmente *.txt
, o que espero que não seja.
rename
não interpreta padrões glob como *.txt
, *.DAT
, *.dat
, x*
, *y
, ou *
quando passado para ele como argumentos pathname. Em vez disso, seu shell realiza a expansão do nome do caminho neles (que também é chamada de expansão de nome de arquivo e também chamada globbing). Isso acontece antes que o rename
utilitário seja executado. O shell expande os globs para nomes de caminho potencialmente múltiplos e passa todos eles, como argumentos separados da linha de comando, para rename
. No Ubuntu, seu shell interativo é o Bash , a menos que você o tenha alterado, e é por isso que vinculei o manual de referência do Bash acima.
Há uma situação em que um padrão glob pode ser passado como um único argumento de linha de comando não expandido para rename
: quando ele não corresponde a nenhum arquivo. Cascas diferentes exibem um comportamento padrão diferente nessa situação, mas o comportamento padrão de Bash é simplesmente passar a bola literalmente. No entanto, você raramente quer isso! Se você o desejou, assegure-se de que o padrão não seja expandido, citando -o. Isso vale para passar argumentos para qualquer comando, não apenas para rename
.
A citação não é apenas para globbing (expansão de nome de arquivo), porque há outras expansões que seu shell executa em texto não citado e, para algumas delas, mas não outras , também em texto entre "
"
aspas. Em geral, sempre que você quiser passar um argumento que contenha caracteres que possam ser tratados especialmente pelo shell, incluindo espaços, você deve citá-lo, de preferência com '
'
aspas .
O código Perl s/foo/bar/
não contém nada tratado especialmente pelo shell, mas seria uma boa idéia para mim ter citado isso também - e escrito 's/foo/bar/'
. (Na verdade, a única razão que eu fiz não foi que seria confuso para alguns leitores, como eu ainda não tinha falado sobre citando.) A razão de eu dizer isso teria sido bem é porque é muito comum que o código Perl não contêm esses caracteres e, se eu mudar esse código, talvez não me lembre de verificar se era necessário citar. Por outro lado, se você deseja que o shell expanda um globo, ele não deve ser citado.
3. O que o intérprete Perl quer dizer com "palavra de barra não permitida"
As mensagens de erro que você mostrou na sua pergunta revelam que, quando você executava rename *.DAT *.dat
, seu shell se expandia *.DAT
para uma lista de um ou mais nomes de arquivos e que o primeiro era b1.DAT
. Todos os argumentos subseqüentes - quaisquer outros expandidos *.DAT
e expandidos de - *.dat
vieram após esse argumento, portanto, eles teriam sido interpretados como nomes de caminho.
Como o que realmente foi executado era algo como rename b1.DAT ...
, e porque rename
trata seu primeiro argumento de não opção como código Perl, a pergunta é: por que b1.DAT
esses erros de "palavra de barra não são permitidos" são produzidos quando você o executa como código 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).
Em um shell, citamos nossas strings para protegê-las de expansões não intencionais do shell que, de outra forma, as transformariam em outras strings automaticamente (consulte a seção acima). Os shells são linguagens de programação para fins especiais que funcionam de maneira muito diferente das linguagens de uso geral (e sua sintaxe e semântica muito estranhas refletem isso). Mas o Perl é uma linguagem de programação de uso geral e, como a maioria das linguagens de programação de uso geral, o principal objetivo de citar no Perl não é proteger as strings, mas mencioná-las. Essa é realmente a maneira pela qual a maioria das linguagens de programação é semelhante à linguagem natural. Em inglês, e supondo que você tenha um cachorro, "seu cachorro" é uma frase de duas palavras, enquanto seu cachorro é um cachorro. Da mesma forma, em Perl, '$foo'
é uma string, enquanto $foo
é algo cujo nome é $foo
.
No entanto, diferentemente de qualquer outra linguagem de programação de uso geral, o Perl também às vezes interpreta o texto não citado como mencionando uma string - uma string que é a "mesma" que ela, no sentido de que é composta dos mesmos caracteres em a mesma ordem. Ele só tentará interpretar o código dessa maneira se for uma palavra de barra (nenhum $
ou outro sigilo, veja abaixo) e depois não encontrar outro significado para fornecê-lo. Em seguida, ele será interpretado como uma sequência, a menos que você o solicite, ativando as restrições .
As variáveis no Perl geralmente começam com um caractere de pontuação chamado sigil , que especifica o tipo amplo da variável. Por exemplo, $
significa escalar , @
meios de matriz , e %
meios de hash . ( Existem outros. ) Não se preocupe se você achar isso confuso (ou chato), porque só estou dizendo que quando um nome válido aparece em um programa Perl, mas não é precedido por um sigilo, esse nome é dito ser uma palavra de baralho .
As palavras de barra servem a vários propósitos, mas geralmente significam uma função interna ou uma sub-rotina definida pelo usuário que foi definida no programa (ou em um módulo usado pelo programa). O Perl não possui funções internas chamadas b1
ou DAT
, portanto, quando o interpretador Perl vê o código b1.DAT
, ele tenta tratar b1
e DAT
como os nomes das sub-rotinas. Assumindo que nenhuma sub-rotina foi definida, isso falha. Em seguida, desde que as restrições não tenham sido ativadas, elas serão tratadas como cadeias. Isso funcionará, embora você realmente pretenda que isso aconteça seja uma incógnita. O .
operador do Perl concatena as strings , portanto, b1.DAT
avalia para a stringb1DAT
. Ou seja, b1.DAT
é uma maneira ruim de escrever algo como 'b1' . 'DAT'
ou "b1" . "DAT"
.
Você pode testar isso sozinho executando o comando perl -E 'say b1.DAT'
, que passa o script Perl curto say b1.DAT
para o interpretador Perl, que o executa, imprimindo b1DAT
. (Nesse comando, as '
'
cotações de dizer ao shell para passar say b1.DAT
como um único argumento de linha de comando, caso contrário, o espaço poderia causar say
e b1.DAT
deve ser analisado como palavras separadas e perl
iria recebê-los como argumentos separados. perl
Se não se vêem as aspas, como o shell os remove .)
Mas agora, tente escreveruse strict;
no script Perl antes say
. Agora ele falha com o mesmo tipo de erro que você obteve 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.
Isso aconteceu porque use strict;
proibiu o intérprete Perl de tratar palavras de baralho como seqüências de caracteres. Para proibir esse recurso em particular, seria realmente suficiente permitir apenas a subs
restrição. Este comando produz os mesmos erros que acima:
perl -E 'use strict "subs"; say b1.DAT'
Mas geralmente os programadores Perl apenas escrevem use strict;
, o que habilita a subs
restrição e mais duas. use strict;
é geralmente uma prática recomendada. Portanto, o rename
comando faz isso para o seu código. É por isso que você recebe essa mensagem de erro.
4. Em resumo, foi o que aconteceu:
- Seu shell passou
b1.DAT
como o primeiro argumento da linha de comando, que rename
tratou como código Perl para executar em um loop para cada argumento do nome do caminho.
- Isso foi considerado como significante
b1
e DAT
conectado ao .
operador.
b1
e DAT
não eram prefixados com sigilos, portanto eram tratados como palavras de baralho.
- Essas duas palavras de barra teriam sido tomadas como nomes de funções internas ou quaisquer sub-rotinas definidas pelo usuário, mas nenhuma dessas coisas tinha nenhum desses nomes.
- Se os "subs estritos" não tivessem sido ativados, eles teriam sido tratados como as expressões de string
'b1'
e 'DAT
'e concatenados. Isso está longe do que você pretendia, o que ilustra como esse recurso geralmente não é útil.
- Mas "subs rigorosas" foi habilitado, porque
rename
permite que todas as restrições ( vars
, refs
, e subs
). Portanto, você recebeu um erro.
rename
sair devido a este erro. Como esse tipo de erro ocorreu mais cedo, nenhuma tentativa de renomeação de arquivo foi feita, mesmo que você não tenha passado -n
. Isso é bom, pois geralmente protege os usuários contra alterações não intencionais do nome do arquivo e, ocasionalmente, até contra a perda real de dados.
Agradeço a Zanna , que me ajudou a abordar várias deficiências importantes em um rascunho mais detalhado desta resposta . Sem ela, essa resposta teria feito muito menos sentido e talvez não fosse divulgada.