Como remover o (1) dos nomes de arquivos usando o comando find

13

Recentemente, converti todos os meus arquivos FLAC para uma taxa de amostragem menor de 44,1 kHz e profundidade de bits de 24 bits (porque o iPhone / iPod não suporta nada acima disso) usando XLD no meu Mac OS 10.7 (Lion).

Embora eu tenha dito ao XLD para substituir todos os arquivos anteriores, o XLD anexou um (1)no final do mesmo arquivo, como de

some_song.m4a

para

some_song(1).m4a

Então agora eu quero remover isso (1)de todos os arquivos FLAC que converti.

Sei que provavelmente poderia ter usado algum programa ou mesmo um AppleScript para renomear os arquivos, mas queria aprender usando a maneira antiga de linha de comando.

Eu sei que find . -name *\(1\).m4avai pegar todo o arquivo FLAC convertido.

Em seguida, sei que tenho que fazer algo com -exece mvrenomear todos os arquivos encontrados. Mas o que não consigo descobrir é como manter o nome do arquivo original e apenas remover o arquivo (1).

Talvez eu precise fazer alguma captura de regex de grupo para armazenar a parte do nome do arquivo que não quero modificar? Ou talvez não seja possível fazer tudo em uma linha e eu deva criar um script de shell (o que não me agrada tanto, mas estou disposto a tentar).

Todas as dicas ou sugestões são bem-vindas! Obrigado!

hobbes3
fonte
5
Por que o voto negativo? Parece uma pergunta válida ...
não relacionado à sua pergunta específica ( ativadafind ), mas que pode estar resolvendo o seu problema real (conversão de arquivos de áudio), talvez você esteja interessado em ver audiotools.sourceforge.net e neste exemplo de caso (para macosx lion) invibe.net/ LaurentPerrinet / SciBlog / 22-04-2012
meduz

Respostas:

13

Não tente analisar a findsaída, exceto como último recurso. É importante perceber que, nos sistemas de arquivos Unix, os nomes de arquivos não são cadeias (um equívoco comum), mas blobs binários que podem conter qualquer caractere, exceto /o caractere nulo. Analisar nomes de arquivos com segurança e corretamente é uma dor suficiente para que 99% das vezes você queira evitar fazê-lo completamente (veja como a sedexpressão na resposta do @ yarek é cabeluda e até isso não cobre todos os casos) . Felizmente, neste caso, há uma abordagem muito mais simples:

find . -name '*(1).m4a' -execdir sh -c \
'for arg; do mv "$arg" "${arg%(1).m4a}".m4a; done' _ {} \+
jw013
fonte
abordagem arrumado ainda como peludo como a expressão sed :) +1
Yrk
1
algo está faltando aqui ... onde está done?
Yrk
@yarek Obrigado pela captura. A maior diferença entre isso e a sedabordagem de pipe é que isso é muito mais seguro. Ainda é mais fácil ler do que a sopa de barra invertida regex, desde que você compreenda algumas construções básicas de script de shell.
Jw013
Você está certo; isso é muito mais limpo. E a $argvariável facilita muito a leitura.
hobbes3
1
@DQdlM O trecho que publiquei acima está apenas findinvocando shcom uma sintaxe POSIX bastante portátil. Para obter mais detalhes, consulte a seção Expansão de parâmetros , a seção sobre comandos compostos que explica o forloop e a especificação POSIXfind . Além desses recursos, eu ficaria feliz em responder a quaisquer perguntas específicas que você tiver.
Jw013
6

No Debian e Ubuntu, eu posso usar rename 's/\(1\)//' *.m4apara resolver seu problema.

Dom
fonte
Estranho, eu posso fazer man rename, mas na verdade não tenho rename: -bash: rename: command not found( which renamenão mostra nada).
hobbes3
@ hobbes3 no mac man -a renameencontra aqui renomear (2) - o syscall e renomear (n) - o comando TCL. Não há renomear (1) nem o próprio utilitário lá.
Yrk
1
IIRC, renameé um script perl incluído nos exemplos incluídos em algumas instalações perl, e algumas distribuições o incluem no caminho porque é um comando conveniente. (@Hobbes talvez você pode encontrá-lo na internet)
Kevin
@ Kevin no meu sistema renameé binário normal (não perl). No entanto, outra ressalva é que nem todas as versões de renamesuporte regexes. A versão que eu tenho, por exemplo, só lhe permite fazer substituições de corda diretos, por exemplorename '(1)' '' *.m4a
Patrick
1
O renamescript Perl é instalado apenas pelo Debian e derivados. Outros sistemas Linux têm um renameutilitário diferente (mostrado por Patrick). OSX não tem nenhum.
Gilles 'SO- stop be evil'
4

No zsh, usando o zmv :

autoload zmv      # you can put this line in your .zshrc
zmv '(*)\(1\)(*)' '$1$2'

No segundo argumento (o novo nome) $1e $2consulte os grupos entre parênteses (PATTERN)no padrão de origem. Outra maneira de escrever essa renomeação é

zmv '(*)' '${1/\(1\)/}'
Gilles 'SO- parar de ser mau'
fonte
3

A abordagem a seguir permite visualizar / remover os comandos gerados antes de executá-los, e é muito portátil: deve funcionar não apenas em um mac, não apenas no bash, e não apenas no GNU sed; mesmo em sistemas sem o findcomando (1), é possível substituí-lo por du(1) sem problemas.

find . -name '*(1).m4a' |
sed 's/\(.*\)(1).m4a$/mv & \1.m4a/' 

Se estiver satisfeito com os comandos impressos, execute novamente com o | sh -xanexo.

Se estiver preocupado com os espaços nos nomes dos arquivos, adicione outro s para escapar de todos os espaços:

find . -name '*(1).m4a' |
sed -e 's/ /\\ /g' -e 's/\(.*\)(1).m4a$/mv & \1.m4a/'

Se outros caracteres especiais são esperados, fica um pouco mais complicado:

find . -name '*(1).m4a' |
sed -e "s/'/'\\\\''/g" -e 's/\(.*\)(1).m4a$/mv '\''&'\'' '\''\1.m4a'\'/

A primeira função converte tudo 'em uma forma que é feita literalmente quando está no meio da '...'cadeia de caracteres com escape. A segunda função gera comandos mv cujos argumentos estão dentro '...'.

yrk
fonte
1
Esse comando não está escapando do nome do arquivo (os espaços (, e todos os outros caracteres especiais) ou adicionando aspas ao redor do nome do arquivo. Tentei modificar o seu comando, mas não consegui descobrir como adicionar aspas nos dois nomes de arquivos do mvcomando. Uma das saídas resultantes do seu comando é mv ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us(1).m4a ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us.m4a. E se eu executar esse comando, recebo -bash: syntax error near unexpected token '('.
hobbes3
isto é suposto ser uma lição de casa :) mas ok, eu vou atualizar a resposta
Yrk
A propósito, o GNU sed pode executar o próprio ecomando adicionando comando após a substituição. Por exemplo find . -name '*(1).m4a' | sed 's/\(.*\)(1).m4a$/mv & \1.m4a/e', executará o comando sem canalizar para sh -x.
apressar
@Rush é o único sed que faz isso; e ele precisa para iniciar o shell de qualquer maneira, mas um novo para cada comando (lembra o do make problema, não é?)
Yrk
2
Eu não acho que deveríamos estar com voto negativo. Afinal, é uma solução válida.
hobbes3
1

Aqui está um pequeno script que faz isso:

for var in `find . -type f -name "*(1).m4a"`; do
    new=`echo $var | cut -d'(' -f1`;
    mv $var $new.m4a;
done
anupam
fonte
este engasgará com arquivos com espaços em seus nomes; também requer armazenamento temporário grande o suficiente para o`find ...`
Yrk