Para loop com unix find

5

Tentando fazer uma conversão em massa de M4A para OGG em uma coleção de músicas grande, eu tenho:

#!/bin/sh
for i in 'find /home/family/Music -name *.m4a -print0'
   #do ffmpeg -i "$i" -acodec libvorbis -aq 6 -vn -ac 2 "$i.ogg";
   do echo $i
done

Todos os arquivos terão espaços em seus nomes, a saída acima mostra um único arquivo como este:

/home/family/Music/The
Kooks/Inside
In
_
Inside
Out/06
You
Don't
Love
Me.m4a

Todo espaço marca uma nova linha, achei que -print0 consertaria isso?

    
por wjdp 01.10.2014 / 19:11

3 respostas

15

Essa é uma das razões pelas quais você nunca usa um loop for para iterar sobre um comando cuja saída pode conter espaços. Especialmente se essa saída for uma lista de nomes de arquivos que podem conter qualquer , exceto / e while . Você caiu no pitfall número 1 . Sempre use *.mp4a . Para ter certeza de que funciona com todos os nomes de arquivos, incluindo aqueles com espaços, novas linhas, tabulações, barras invertidas ou qualquer outro caractere estranho, use:

find /home/family/Music -name '*.m4a' -print0 | while IFS= read -r -d '' file; do
     ffmpeg -i "$file" -acodec libvorbis -aq 6 -vn -ac 2 "${file%.m4a}.ogg";
done

Explicação

  • Observe que eu citei find , o que garante que o bash não irá expandi-lo antes de passá-lo para -print0 . Isso é importante para os casos em que você possui arquivos que correspondem ao glob no diretório atual.

  • O find , como você provavelmente sabe, faz com que IFS= separe seus resultados com while read -r -d '' file em vez de novas linhas.

  • $file : isso define o caractere do campo de entrada como nada, garantindo que nenhuma divisão de palavras ocorra.

  • for file in $(command) : iterará os resultados, salvando cada um como help read , como -d '' . As opções são (de read ):

     -r     do not allow backslashes to escape any characters
     -d delim   continue until the first character of DELIM is read, rather
        than newline
    

    Configurar o delimitador para a string vazia ( -print0 ) torna "${file%.mp3}.ogg"; legal com .m4a . do find.

  • .ogg : é apenas para remover o sufixo foo.ogg e substituí-lo por foo.m4a.ogg , para obter %code% em vez de %code% .

O resto é o mesmo que você tentou, então estou supondo que você entendeu.

    
por terdon 01.10.2014 / 19:34
8

Use xargs com a opção -0 ou use < href="http://manpages.ubuntu.com/manpages/trusty/man1/find.1.html"> find possui a opção exec :

find /home/family/Music -name '*.m4a' -exec ffmpeg -i "{}" -acodec libvorbis -aq 6 -vn -ac 2 "{}.ogg" \;
# or:
find /home/family/Music -name '*.m4a' -print0 | xargs -0 -i ffmpeg -i {} -acodec libvorbis -aq 6 -vn -ac 2 {}.ogg

Observe que, em ambos os casos (e em seu comando original), x.m4a será convertido em x.m4a.ogg .

    
por muru 01.10.2014 / 19:35
2

Isso pode ser uma solução do que você quer

#!/bin/bash    
find  /home/family/Music -type f -name '*.m4a' -print0 | while IFS= read -r -d '' i; 
  do
   #do ffmpeg -i "$i" -acodec libvorbis -aq 6 -vn -ac 2 "$i.ogg"; 
   echo $i
done
    
por g_p 01.10.2014 / 19:35