Usando xargs com $ () - precedência do operador?

3

Inspirado por esta questão . A questão original, em suma, é "Como transformar todos os :2f em - em todos os arquivos dentro de uma pasta". por exemplo. Se eu tiver um arquivo ./abc:2fdef , ele deverá ser renomeado para ./abc-def .

Primeiro, achei que era uma tarefa simples ... find de todos os arquivos e, em seguida, sed para substituir :2f em - . Ao tentar construir um one-liner, chego ao comando:

find . -type f -name '*:2f*' | xargs -I {} mv {} $(echo {} | sed "s/:2f/-/ig")

No entanto, isso não funciona. Depois de fazer muitos testes, descobri que o problema está no xargs part - $ () foi executado antes de xargs place substituir {} pelos nomes dos arquivos. Para elaborar,

xargs -I {} mv {} $(echo {} | sed "s/:2f/-/ig")

foi avaliado como ( $(...) foi avaliado)

xargs -I {} mv {} {}

e depois

mv ./abc:2fdef ./abc:2fdef

O que não era o que estou esperando. Então, minha pergunta é: posso fazer xargs substituir todos os {} em nomes de arquivos antes de avaliar a $(...) parte?

    
por Kenneth L 16.07.2014 / 12:08

1 resposta

5

Não, porque xargs não pode substituir algo antes do shell chamar xargs com $(…) já avaliado. Isso se deve à ordem da expansão do shell .

Eu provavelmente faria assim:

find . -type f -name '*:2f*' -exec bash -c 'mv -- "$0" "${0//:2f/-}"' {} \;

Aqui, a chamada do shell é avaliada em todos os arquivos encontrados por find , e a substituição da string é feita com recursos de shell em vez de outra chamada para sed .

Além disso, essa abordagem é mais segura porque os espaços em nomes de arquivos são manipulados adequadamente (por meio da duplicação dos argumentos para a chamada mv ). Mais sobre isso aqui .

Para essas tarefas, você provavelmente precisará de uma ferramenta mais eficiente, como zmv , disponível em Zsh, ou o Perl rename , que é empacotado com muitas distribuições Linux:

rename 's/:2f/-/' *
    
por 16.07.2014 / 12:25