Renomeação em massa de arquivos camelcase para incluir espaços

5

Estou tentando renomear um lote de arquivos com o nome camelcase para incluir espaços entre letras maiúsculas e minúsculas adjacentes. Estou usando o Mac OS para que os utils que estou usando sejam da variante BSD.

Por exemplo:

250 - ErosPhiliaAgape.mp3 => 250 - Eros Philia Agape.mp3

Estou tentando find dos arquivos relevantes e canalizo-os para mv , que executa sed em um subshell usando este comando:

find . -name "*[a-z][A-Z]*" -print0 | xargs -0 -I {} mv {} "$(sed -E 's/([a-z])([A-Z])/ /g')"

Individualmente, esses comandos funcionam bem: find obtém os arquivos corretos e sed renomeia corretamente, mas quando eu os combino com xargs, nada acontece.

O que preciso mudar para que isso funcione?

    
por Chris Wilson 23.12.2016 / 21:22

3 respostas

4

O problema é que a sub-shell $(...) em seu comando é avaliada no momento em que você executa o comando e não é avaliada por xargs para cada arquivo. Você pode usar find -exec para avaliar os comandos de cada arquivo em sh e também substituir as citações apropriadamente:

find . -name "*[a-z][A-Z]*" -type f -exec sh -c 'echo mv -v "{}" "$(echo "{}" | sed -E "s/([a-z])([A-Z])/ /g")"' \;

Se a saída estiver boa, elimine o echo in echo mv . Observe que, devido a echo | sed , isso não funcionará com nomes de arquivos com \n incorporado. (Mas essa foi uma limitação já existente em sua própria tentativa, então espero que seja aceitável.)

    
por 23.12.2016 / 21:45
3

Seu comando sed está recebendo dados do canal. Ele está competindo com xargs , então cada um dos dois comandos terá parte dos dados escritos por find (cada byte vai para apenas um leitor), e é imprevisível que recebe o quê.

Você precisaria enviar cada nome de arquivo para sed , o que significa que você precisa de um shell intermediário para configurar o pipe.

find . -name "*[a-z][A-Z]*" -print0 | xargs -0 -I {} sh -c 'mv "$0" "$(echo "$0" | sed -E '\''s/([a-z])([A-Z])/ /g'\'')"' {}

Você não precisa de xargs , é basicamente inútil. find pode chamar um programa diretamente.

find . -name "*[a-z][A-Z]*" -exec sh -c 'mv "$0" "$(echo "$0" | sed -E '\''s/([a-z])([A-Z])/ /g'\'')"' {} \;

Como alternativa, instale qualquer um dos Perl rename implementações, por exemplo, File::Rename ou o nome de Unicode::Tussle .

cpan File::Rename
find -depth . -exec rename 's!([a-z])([A-Z])(?!.*/)!$1 $2!g' {} +

O (?!.*/) bit impede que a substituição seja acionada em nomes de diretório, caso contenham camelcase. A passagem de -depth para find garante que, se um diretório for renomeado, isso acontecerá depois que find tiver passado por ele.

    
por 25.12.2016 / 01:53
1

Se você preferir usar o GNU Parallel, a solução é:

find . -name "*[a-z][A-Z]*" -type f | parallel mv -v {} '{= s:([a-z])([A-Z])(?!.*/):$1 $2:g =}'
    
por 04.03.2017 / 12:18