Como renomear múltiplos arquivos usando find

28

Eu quero renomear múltiplos arquivos (file1 ... filen para file1_renamed ... filen_renamed) usando o comando find command:

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

Mas recebendo este erro:

mv: cannot stat ‘filename=./file1’: No such file or directory

Isso não funciona porque o nome do arquivo não é interpretado como variável de shell.

    
por user164014 05.09.2015 / 08:03

4 respostas

39

O que se segue é uma correção direta da sua abordagem:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

No entanto, isso é muito caro se você tiver muitos arquivos correspondentes, porque você inicia um novo shell (que executa mv ) para cada correspondência. E se você tiver personagens engraçados em qualquer nome de arquivo, isso explodirá. Uma abordagem mais eficiente e segura é esta:

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

Também tem o benefício de trabalhar com arquivos com nomes estranhos. Se find suportar, isso pode ser reduzido para

find . -type f -name 'file*' -exec mv {} {}_renamed \;

A versão xargs é útil quando você não usa {} , como em

find .... -print0 | xargs --null rm

Aqui rm é chamado uma vez (ou com vários arquivos várias vezes), mas não para cada arquivo.

Eu removi o basename em sua pergunta porque provavelmente está errado: você moveria foo/bar/file8 para file8_renamed , não foo/bar/file8_renamed .

Edições (como sugerido nos comentários):

  • % abreviada find sem xargs
  • Adicionado adesivo de segurança
por 05.09.2015 / 09:19
16

Depois de tentar a primeira resposta e brincar com ela um pouco, descobri que isso pode ser feito um pouco mais curto e menos complexo usando -execdir :

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

Parece que também deve fazer exatamente o que você precisa.

    
por 09.02.2016 / 18:07
4

Outra abordagem é usar um loop while read sobre find output. Isso permite o acesso a cada nome de arquivo como uma variável que pode ser manipulada sem ter que se preocupar com problemas adicionais de custo / potencial de segurança de gerar um processo sh -c usando a opção find -exec .

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

E se o shell que está sendo usado suportar a opção -d para especificar um read delimitador, você poderá suportar arquivos com nomes estranhos (por exemplo, com uma nova linha) usando o seguinte:

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done
    
por 04.11.2017 / 20:34
3

Desejo expandir em a primeira resposta e observe que isso não funcionará para anexar ao nome do arquivo, pois o prefixo ./ do caminho está presente no argumento do nome do arquivo.

Modificando a resposta de Thomas Erker, acho que esta é uma abordagem mais genérica

find . -name PATTERN -printf "%f
find . -name PATTERN -printf "%f%pre%" | xargs --null -I{} mv {} "prefix {} suffix"
" | xargs --null -I{} mv {} "prefix {} suffix"

opções xargs:

--null Indica que cada argumento transmitido por stdin termina com um caractere nulo ( mv ). Desta forma, o nome do arquivo pode conter espaços, caso contrário, cada palavra será ameaçada como um parâmetro diferente para o comando -I replace-str .

replace-str Toda ocorrência de stdin será substituída pelo argumento lido em %code% . Então, você pode alterá-lo para outra string, se precisar.

    
por 17.01.2017 / 18:43

Tags