Uso do comando find para renomear arquivos

3

Eu tenho uma lista de arquivos de 10K mp4, alguns dos quais contém um? marca. Eu preciso substituir estes com _ (sublinhado).

Eu posso usar o comando find para encontrar esses arquivos, mas não tenho certeza se posso renomeá-los.

find . -name "*\?*.mp4" -exec mv {} XXXXXXX \;

Parece que tenho que percorrer a lista retornada por find e para cada um que preciso substituir? com _ mas eu não sei como fazer isso.

    
por tintin 20.06.2015 / 20:36

5 respostas

4

Não use find para isso. A maneira como você for com find precisará de algo como um único mv por arquivo. Isso é um monte de processos e sem nenhum benefício. Isso sem mencionar que é simplesmente mais difícil fazer isso. Essa é a minha opinião, de qualquer maneira.

Eu usaria um stream ou uma ferramenta em lote e tenderia a preferir pax :

cd -P . && mkdir ../newmp4 &&
pax -rwls'|?|_|g' . "${PWD%/*}/newmp4"

Se você puder criar o diretório ../newmp4 , esse pequeno script espelhará a árvore com raiz em . para o diretório recém-criado com hardlinks, mas substituirá cada ? que ocorre em qualquer nome de arquivo por um sublinhado no enquanto isso.

Uma vantagem para isso é que as árvores ambas continuam a existir depois - nenhuma das pastas raiz do diretório atual são afetadas - elas são alteradas apenas na versão espelhada da árvore. E assim você pode inspecionar os resultados depois de decidir qual versão da árvore remover. Se alguma coisa der errado durante a operação - não há mal nenhum.

Isso requer, no entanto, um fs que suporte hardlinks e que tenha pelo menos o mesmo número de inodes livres restantes, pois há diretórios filhos de . +2 e assume que .. . e todos os filhos de . compartilha a mesma montagem do sistema de arquivos.

Praticamente o mesmo efeito pode ser obtido com rsync , eu acho, embora eu não acredite que seja simplesmente feito. Se você não tem pax (mesmo se você realmente deveria ) , você pode simplesmente obter isto. mirabilos mantém o (que eu saiba) mais versão comumente usada nos sistemas linux.

    
por 20.06.2015 / 23:20
2

Pessoalmente, eu acho que isso apenas clama por perl, caractere especial seguro e renomear é uma chamada de sistema, não um fork.

find . -name "*\?*.mp4" -print0|perl -n0e 'next if /\?.*\//;$o=$_;s!\?!_!g;next if -e;rename $o, $_'

Ignora arquivos em diretórios com? em seu nome para evitar comportamento indefinido e verifica conflitos de renomeação. Veja abaixo uma explicação detalhada.

find . \               # find files below the current directory
    -name "*\?*.mp4" \ # having a ? in the name and ending in .mp4
    -print0\           # print the file names separated by nulls
|perl \                # direct the output of find to the input of perl
    -n \               # loop over standard input while assigning it to $_
    -0 \               # lines from standard input are separated by null
    -e \               # evaluate the next argument as perl commands
    'next if /\?.*\//; # skip this file if the directory name includes ?
    $o=$_;             # make a copy of the file name of preserve the name
    s!\?!_!g;          # change the ?s in the filename to _s
    next if -e;        # skip file if there is a file by that name
    rename $o, $_'     # do the rename
    
por 21.06.2015 / 00:14
1

A maneira mais fácil que eu encontrei para fazer isso é canalizar algo diferente para gerar comandos, como sed , e então executar os comandos em sh .

find . -name '*\?*.mp4' -print | sed 's/.*/"&";h;y/?/_/;x;G;s/\n/ /;s/^/mv /' | sh -s

O find é bastante autoexplicativo, assim como o sh . O comando sed pode dar uma pequena explicação:

  • s/.*/"&"/ envolve a entrada com aspas duplas para manipular qualquer espaço em branco ou caracteres especiais;
  • h copia o buffer para o buffer "hold", salvando uma cópia;
  • y/?/_/ é o mesmo que tr \? _ na linha de comando;
  • x troca o conteúdo do buffer "hold" pelo buffer padrão;
  • G acrescenta o buffer "hold" ao final do buffer padrão com um separador '\ n'; agora temos "stringwith? \ nstringwith _";
  • s/\n/ / substitui a nova linha;
  • s/^/mv / prepends o comando mv .

Isso gera mv de comandos para cada arquivo encontrado por find com o nome antigo e o novo nome.

    
por 20.06.2015 / 20:50
1

Você pode usar prename com find :

find . -type f -name '*\?*.mp4' -exec prename -n 's:(.*/[^?]*)\?(.*$):$1_$2:' {} +

prename -n mostrará os arquivos que serão renomeados, se você estiver satisfeito com isso, execute o comando removendo -n (ou seja, prename 's/\?/_/' ).

    
por 20.06.2015 / 20:50
1

com zsh :

autoload zmv # best in ~/.zshrc
zmv '(**/)(*\?*.mp4)' '$1${2//\?/_}'

Isso exclui os ocultos e não examina os diretórios ocultos. Ele irá renomear esses arquivos independentemente do seu tipo (regular, diretórios, links simbólicos ...).

Se você também quiser considerar arquivos ocultos / dirs, apenas renomeie os arquivos mp4 se eles forem arquivos regulares e não diferencie maiúsculas de minúsculas, isso seria:

zmv '(**/)(*\?*.(#i)mp4)(#q.D)' '$1${2//\?/_}'

Uma aproximação GNU (não verificaria conflitos e sobrescreveria como zmv ):

find . -iname '*\?*.mp4' -type f -exec bash -c '
  for file do
    base=${file##*/}
    mv -i "$file" "${file%/*}/${base//\?/_}"
  done' {} +
    
por 20.06.2015 / 22:48

Tags