Eu tenho dois arquivos em uma pasta no meu Ubuntu 16.04:
a1.dat
b1.DAT
Quero renomear b1.DAT
para, b1.dat
portanto, os seguintes arquivos, como resultado, na pasta:
a1.dat
b1.dat
Eu tentei (sem sucesso):
$ 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).
A pesquisa não resultou em nenhuma solução significativa ...
Respostas:
Esse erro parece que vem do Perl
rename
. Você precisa usar a citação, mas precisa especificar apenas a parte que deseja alterar, o estilo de pesquisar e substituir. A sintaxe é assim:Remova
-n
após o teste para renomear os arquivos.Para incluir arquivos ocultos, altere a configuração da glob antes de executar o comando:
Se quiser renomear arquivos recursivamente, você pode usar
ou se houver muitos caminhos dentro ou abaixo do diretório atual que não terminam
.DAT
, é melhor especificar esses caminhos no segundo comando:Isso será mais rápido se seus arquivos tiverem vários nomes diferentes que não terminem
.DAT
. [1]Para desativar essas configurações, você pode usar
shopt -u
, por exemploshopt -u globstar
, mas elas estão desativadas por padrão e serão desativadas quando você abrir um novo shell.Se isso resultar em uma lista de argumentos excessivamente longa, você pode usar, por exemplo
find
:ou melhor
Usar
find ... -exec
com+
é mais rápido do que usar,\;
pois cria uma lista de argumentos a partir dos arquivos encontrados. Originalmente, pensei que não seria possível usá-lo, porque você mencionou que estava tendo umargument list too long
problema, mas agora sei que a lista também será dividida de maneira inteligente em várias invocações do comando, conforme necessário, para evitar esse problema. [2] .Como
rename
processará todos os nomes de arquivos da mesma maneira, não importa quanto tempo a lista de argumentos tenha, pois ela pode ser dividida com segurança em várias invocações. Se o comando que você está usando-exec
não aceitar vários argumentos ou exigir que seus argumentos estejam em uma ordem específica ou por qualquer outro motivo, a divisão da lista de argumentos fará com que algo indesejado aconteça, você poderá usar\;
, o que fará com que o comando seja chamado uma vez para cada arquivo encontrado (se a lista de argumentos for muito longa para outros métodos, isso levará muito tempo!).Muito obrigado a Eliah Kagan pelas sugestões muito úteis para melhorar esta resposta:
[1] Especificando os nomes dos arquivos ao globbing .
[2]
find ... -exec
com+
divide a lista de argumentos .fonte
-n
partir de renomeação depois de testar - é apenas para mostrar o que será alteradoVocê pode fazer:
Descarte
-n
para que a renomeação real ocorra.o padrão glob
*.DAT
corresponde a todos os arquivos que terminam no.DAT
diretório atualna
rename
substituição,DAT$
combinaDAT
no final\L$&
torna a partida inteira em minúsculas;$&
refere-se a toda a partidaSe você apenas quer fazer por
b1.DAT
:Exemplo:
fonte
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 comorename
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 oprename
comando. Nas versões mais recentes, ele aponta para ofile-rename
comando mais recente . Esses dois comandos de renomeação de Perl funcionam da mesma maneira, e vou apenas me referir a ambos comorename
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 dizendorename
para executá-lo. Isso ocorre porque o primeiro argumento de linha de comando que você passar para orename
comando, com excepção argumentos opcionais como-n
, consiste de código Perl real . Orename
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ãorename
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 defoo
na cadeia mantida pela$str
variável parabar
ou a deixa inalterada se não contiverfoo
. Se você acabou de escrevers/foo/bar/
sem usar explicitamente o=~
operador , ele continuará funcionando$_
. Isso quer dizer ques/foo/bar/
é a abreviação de$_ =~ s/foo/bar/
.É comum passar uma
s///
expressão derename
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
, orename
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 orename
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 orename
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, pararename
. 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 pararename
.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 erab1.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 porquerename
trata seu primeiro argumento de não opção como código Perl, a pergunta é: por queb1.DAT
esses erros de "palavra de barra não são permitidos" são produzidos quando você o executa como código Perl?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
ouDAT
, portanto, quando o interpretador Perl vê o códigob1.DAT
, ele tenta tratarb1
eDAT
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 curtosay b1.DAT
para o interpretador Perl, que o executa, imprimindob1DAT
. (Nesse comando, as'
'
cotações de dizer ao shell para passarsay b1.DAT
como um único argumento de linha de comando, caso contrário, o espaço poderia causarsay
eb1.DAT
deve ser analisado como palavras separadas eperl
iria recebê-los como argumentos separados.perl
Se não se vêem as aspas, como o shell os remove .)Mas agora, tente escrever
use strict;
no script Perl antessay
. Agora ele falha com o mesmo tipo de erro que você obteverename
: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 asubs
restrição. Este comando produz os mesmos erros que acima:Mas geralmente os programadores Perl apenas escrevem
use strict;
, o que habilita asubs
restrição e mais duas.use strict;
é geralmente uma prática recomendada. Portanto, orename
comando faz isso para o seu código. É por isso que você recebe essa mensagem de erro.4. Em resumo, foi o que aconteceu:
b1.DAT
como o primeiro argumento da linha de comando, querename
tratou como código Perl para executar em um loop para cada argumento do nome do caminho.b1
eDAT
conectado ao.
operador.b1
eDAT
não eram prefixados com sigilos, portanto eram tratados como palavras de baralho.'b1'
e'DAT
'e concatenados. Isso está longe do que você pretendia, o que ilustra como esse recurso geralmente não é útil.rename
permite que todas as restrições (vars
,refs
, esubs
). 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.
fonte