Como eu significo a repetição em um regex do ZMV?

3

Estou tentando extrair manualmente os digests dos ativos do Rails (não pergunte). Fui direcionado ao ZMV para localizar / substituir baseado em regex fácil. Mas a sintaxe normal de {32} para especificar uma quantidade de repetição não funciona:

$ zmv -n '(**/)(*)' '$1${2//-[A-Za-z0-9]\{32\}/}'

Eu tentei alguns outros formatos. Isso, por exemplo, funciona, mas é muito ganancioso (por exemplo, vai transformar image-3.png em image.png ):

$ zmv -n '(**/)(*)' '$1${2//-[A-Za-z0-9]##\./.}'

Essa sintaxe de hash duplo só apareceu depois de muito googling (eu esperava + ). Mas eu não posso para a vida de mim encontrar como fazer {32} funcionar. Eu tentei #32# ? O que pareceu funcionar, mas é porque ele estava lendo isso (em meus olhos) ?32? e isso significa que ele encontrou qualquer coisa que tivesse três no digest ou no último caractere.

Como eu significo a repetição de caracteres em zmv?

EDITAR:

Aparentemente, isso ajudaria alguns a ver nomes de arquivos que eu estou tentando combinar? Para ser claro: minha pergunta é "como eu significo a repetição de caracteres em zmv" não "como faço para coincidir esses nomes de arquivos" (uma pergunta que eu sei a resposta em padrão Formato RegEx). Se isso ajudar, aqui está minha intenção antes e depois:

directory/asset-jej4jtifne9bjkkeuwr09rewrewlur23.css
another-directory/style-748reiodlpqwerntaerwerwerexfzsdf.js.gz
directory/subdirectory/this-is-a-thing-qwertyuiopasdfghjklzxcvbnm123456.js
third-directory/should-not-match-3.css

Deve se tornar:

directory/asset.css
another-directory/style.js.gz
directory/subdirectory/this-is-a-thing.js
third-directory/should-not-match-3.css

SEGUNDA EDIT:

Porque eu precisava fazer isso ontem, fiz o longo caminho e (como esperado) funcionou. Eu ainda gostaria de saber como evitar isso no futuro. Aqui está o comando que acabei usando (eu repeti meu matcher de personagem 32 vezes explicitamente):

$ zmv '(***/)(*)' '$1${2//-[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9]/}'

TERCEIRA EDIÇÃO:

Para o registro, estou usando o zsh no OS X. Eu imagino que o zmv é o mesmo em todas as plataformas, mas não tenho certeza.

    
por Ben Saufley 26.03.2015 / 22:29

2 respostas

5

Os shells geralmente não fornecem a sintaxe usual de regexp, mas os padrões "glob" de curingas. Os curingas básicos do shell não são tão poderosos quanto as expressões regulares; por exemplo, o regexp .* (qualquer sequência de caracteres) é equivalente ao padrão de glob * , mas o regexp a* (qualquer sequência de a 's) não tem padrão glob equivalente no sh simples. Veja Por que minha expressão regular trabalho em X, mas não em Y? para uma visão geral das principais sintaxes diferentes de regexp / padrão.

Zsh tem padrões de glob estendido zsh que fornecem o mesmo poder expressivo que expressões regulares mas com sintaxe diferente. Esses padrões são habilitados automaticamente em zmv e em funções de conclusão, mas em outros lugares em zsh eles precisam ser habilitados explicitamente com setopt extended_glob (coloque isso em seu .zshrc - a única razão pela qual não é o padrão é compatibilidade com versões anteriores versões antigas do zsh).

Existe uma sintaxe repeat-N-times, mas é um pouco oculta, listada em globbing flags em vez de estar na lista de operadores. É o c flag, que deve ser usado sozinho, seguido pelo número de repetições (ou dois números separados por vírgula para fornecer um intervalo).

zmv -n '(**/)(*)' '$1${2//-[A-Za-z0-9](#c32)/}'
    
por 27.03.2015 / 18:06
1

Também não consigo trabalhar com zmv . Deve haver um caminho, mas isso me escapa. No entanto, zmv não é a única ferramenta que pode fazer algo assim. Você também pode usar rename .

  • Se você estiver usando zsh

    $ rename -n 's/-[A-Za-z0-9]{32}//' **/* 
    another-directory/style-748reiodlpqwerntaerwerwerexfzsdf.js.gz renamed as another-directory/style.js.gz
    directory/asset-jej4jtifne9bjkkeuwr09rewrewlur23.css renamed as directory/asset.css
    directory/subdirectory/this-is-a-thing-qwertyuiopasdfghjklzxcvbnm123456.js renamed as directory/subdirectory/this-is-a-thing.js
    
  • Se você estiver usando bash

    $ shopt -s globstar 
    $ rename -n 's/-[A-Za-z0-9]{32}//' **/* 
    another-directory/style-748reiodlpqwerntaerwerwerexfzsdf.js.gz renamed as another-directory/style.js.gz
    directory/asset-jej4jtifne9bjkkeuwr09rewrewlur23.css renamed as directory/asset.css
    directory/subdirectory/this-is-a-thing-qwertyuiopasdfghjklzxcvbnm123456.js renamed as directory/subdirectory/this-is-a-thing.js
    

Observe que há dois comandos rename no mundo do Linux. Os exemplos acima usam o Perl, que é o padrão nas distribuições baseadas no Debian.

O motivo pelo qual você não conseguiu fazer isso funcionar com zmv é que i) não é zmv que interpreta a expressão, que é um recurso de shell e, portanto, ii) não é uma expressão regular, é um glob .

Quando você executa o comando em sua pergunta, zmv define $2 para cada um dos nomes de arquivos e, em seguida, é o shell que executa a substituição ( ${2//... ). Uma vez que a variável foi expandida pelo shell, ela é retornada para zmv , que tenta a operação de renomeação.

Como o shell e o bash do korn, o suporta o formato ${foo//bar} que removerá todas as correspondências do glob bar da variável $foo (contraste com ${foo/bar} , o que removerá apenas a primeira correspondência). Funciona assim:

% foo="Xababab"
% echo ${foo//ab}
X
% echo ${foo//a*b}
X

Como você pode ver acima, os padrões são globs e não expressões regulares. O glob a*b significa "corresponde a a , depois 0 ou mais caracteres e, em seguida, b ". É o equivalente a essa expressão regular: a.*b . Globs, ao contrário de regexes, não suportam repetição (aparentemente, os globs de zsh, veja a resposta de Gilles ). A sintaxe x{n} não corresponderá a n repetições de x. Portanto, é por isso que seu regex falhou: ele não estava sendo interpretado como um regex!

    
por 27.03.2015 / 17:13