Sou relativamente novo no Bash e estou tentando fazer algo que na superfície parecia bastante simples - execute a busca por uma hierarquia de diretórios para obter todos os arquivos * .wma, canalize essa saída para um comando em que os converto para mp3 e salve o arquivo convertido como .mp3. Meu pensamento era que o comando deveria ter a seguinte aparência (deixei de fora o comando de conversão de áudio e, em vez disso, estou usando echo para ilustração):
$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3
Pelo que entendi, o argumento -print0 permitirá que eu lide com nomes de arquivos com espaços (o que muitos deles fazem como arquivos de música). Estou esperando (como resultado do xargs) que cada caminho de arquivo encontrado seja capturado em f, e que usando a substring corresponda / exclua do final da string, que eu deva ecoar o caminho original do arquivo com um mp3 extensão em vez de wma. No entanto, em vez deste resultado, estou vendo o seguinte:
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...
Portanto, minha pergunta (além do específico 'o que estou fazendo de errado aqui') é: valores que resultam de uma operação de pipe precisam ser tratados de maneira diferente nas operações de manipulação de strings do que aqueles resultantes de uma atribuição de variável ?
xargs
comfind
. Ele vem com uma-exec
opção. Você pode simplesmente adicionar o comando que você usará à sua pergunta e alguém pode mostrar ofind
comando correto ?{}
membro)xargs
é mais adequado do queexec
. Consulte este stackpost stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs para um caso em questão.Respostas:
Como outras respostas já identificaram,
${f%.*}
é expandida pelo shell antes de executar oxargs
comando. Você precisa que essa expansão ocorra uma vez para cada nome de arquivo, com a variável shellf
definida como o nome do arquivo (a passagem-I f
não faz isso:xargs
não tem noção de variável do shell, ela procura a stringf
no comando, portanto, se você usado, por exemploxargs -I e echo …
, teria executado comandos como./somedir/somefile.wmacho .mp3
).Mantendo essa abordagem, diga
xargs
para chamar um shell que possa executar a expansão. Melhor, digafind
-xargs
é uma ferramenta amplamente obsoleta e difícil de usar corretamente, pois as versões modernas do find têm uma construção que faz o mesmo trabalho (e mais) com menos dificuldades de encanamento. Em vez defind … -print0 | xargs -0 command …
, corrafind … -exec command … {} +
.O argumento
_
está$0
no shell; os nomes dos arquivos são passados como argumentos posicionais que sefor f; do …
repetem. Uma versão mais simples deste comando executa um shell separado para cada arquivo, que é equivalente, mas um pouco mais lento:Você realmente não precisa usar
find
aqui, supondo que você esteja executando um shell razoavelmente recente (ksh93, bash ≥4.0 ou zsh). No bash, coloqueshopt -s globstar
no seu.bashrc
para ativar o**/
padrão glob para se repetir em subdiretórios (em ksh, isso éset -o globstar
). Então você pode correr(Se você tiver diretórios chamados
*.wma
, adicione[ -f "$f" ] || continue
no início do loop.)fonte
No caso de sua avaliação da solução de $ {f%. *}. Mp3, é feita no shell em que você está invocando todo o comando, não no shell bifurcado pelo xargs . E no seu shell não há variável f , ela é substituída por uma string vazia.
Solução usando xargs com -I f :
Mas eu faria assim:
fonte