Por que esses arquivos clobber de uma linha?

3

Eu escrevi isso para me ajudar a codificar em lote alguns vídeos:

find -name '*.mkv' -exec HandBrakeCLI -Z "Android Tablet" \
  -c "1-3" -m -O --x264-tune film -E copy --decomb -s 1 -i {} \
  -o 'echo {} | tr mkv m4v' \;

No entanto, falha catastroficamente - leva cada arquivo 700-900MB e o substitui por um arquivo de 36KB.

Notavelmente, o HandBrake é capaz de ler e imprimir as propriedades do arquivo de vídeo; é somente quando ele passa a codificá-lo que ele falha porque o arquivo é inválido. Então parece que a sobregravação ocorre quando chega a ...

-o 'echo {} | tr mkv m4v'

... mas não sei por que deveria. Eu estou simplesmente tentando mudar o nome do arquivo de saída de whatever.mkv para whatever.m4v

Os nomes dos arquivos não possuem espaços. Eles estão na forma 12-the-revenge.mkv , por exemplo.

    
por user45084 14.08.2013 / 05:09

1 resposta

8

A expressão de backticked: echo {} | tr mkv m4v (que é não o que você deseja, por uma variedade de razões; veja abaixo) é expandida uma vez, quando o comando find é analisado. Se você estiver usando bash , normalmente ele será expandido para {} :

$ echo {} | tr mkv m4v
{}

E, de fato, isso acontece em todos os shell instalados na minha máquina, exceto peixes, que geram uma linha vazia. Supondo que você não esteja usando fish , os argumentos que find está realmente vendo serão:

find -name '*.mkv' -exec HandBrakeCLI -Z "Android Tablet" \
-c "1-3" -m -O --x264-tune film -E copy --decomb -s 1 -i {} \
-o {} \;

Em suma, HandBrakeCLI está recebendo o mesmo arquivo para entrada e saída, e acho que é isso que você está vendo, embora sua descrição dos sintomas não seja muito precisa.

Infelizmente, não há uma maneira fácil de encontrar o substituto de extensão que você deseja fazer em uma ação -exec . Você pode fazer isso passando o comando para bash -c , mas isso envolverá uma citação extra e confusa na linha de comando. Uma solução mais limpa e mais legível é criar um arquivo de script de shell que pode iterar seus argumentos:

Arquivo: mkvtom4v.sh (certifique-se de chmod a+x mkvtom4v.sh )

#!/bin/bash
for file in "$@"; do
  HandBrakeCLI -Z "Android Tablet" -c "1-3" -m -O \
               --x264-tune film -E copy --decomb -s 1 \
               -i "$file" -o "${file/%.mkv/.m4v}"
done

E, em seguida, invoque-o com find:

find /path/to/directory -name '*.mkv' -exec /path/to/mkvtom4v.sh {} +

Algumas notas:

  1. Não use backticks ( some command ); eles foram preteridos por muitos anos. Use $(some command) , que é mais legível e permite o aninhamento.

  2. tr definitivamente não é o que você deseja. Faz a tradução de caractere por caractere. Por exemplo, tr aeiou AEIOU faria todas as vWs UppErcAsE. tr mkv m4v mudará todos os k para 4 . Veja man tr (ou info tr ) para mais detalhes.

  3. "${file/%.mkv/.m4v}" é a sintaxe de pesquisa e substituição idiossincrática do bash. Nesse caso, significa: "Pegue o valor de $file e, se ele terminar com .mkv (o % significa" termina com "neste contexto), substitua-o por .m4v ". Existem muitos outros bashisms para editar o valor de uma variável. man bash e procure por "Expansão de Parâmetros".

  4. Com o GNU find, você pode usar {} + no final de um comando -exec (em vez de \; ). Ele será substituído pelo maior número possível de nomes de arquivos encontrados. Veja info find para mais detalhes.

por 14.08.2013 / 06:44

Tags