find: argumento ausente para -exec

206

Fui ajudado hoje com um comando, mas não parece estar funcionando. Este é o comando:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 && rm {}\;

O shell retorna

find: missing argument to `-exec'

O que estou basicamente tentando fazer é percorrer um diretório recursivamente (se houver outros diretórios) e executar o comando ffmpeg nos .rmtipos de arquivo e convertê-los em .mp3tipos de arquivo. Feito isso, remova o .rmarquivo que acabou de ser convertido.

Agradeço qualquer ajuda nisso.

Abdômen
fonte

Respostas:

340

Um -execcomando deve ser finalizado com a ;(portanto, você geralmente precisa digitar \;ou ';'evitar a interpretação pelo shell) ou a +. A diferença é que ;, com , o comando é chamado uma vez por arquivo, com +, é chamado apenas o menor número de vezes possível (geralmente uma vez, mas existe um comprimento máximo para uma linha de comando, portanto pode ser dividido) com todos os nomes de arquivos . Veja este exemplo:

$ cat /tmp/echoargs
#!/bin/sh
echo $1 - $2 - $3
$ find /tmp/foo -exec /tmp/echoargs {} \;
/tmp/foo - -
/tmp/foo/one - -
/tmp/foo/two - -
$ find /tmp/foo -exec /tmp/echoargs {} +
/tmp/foo - /tmp/foo/one - /tmp/foo/two

Seu comando possui dois erros:

Primeiro, você usa {};, mas ;deve ser um parâmetro próprio.

Segundo, o comando termina no &&. Você especificou "executar localização e, se isso tiver sido bem-sucedido, remova o arquivo chamado {};.". Se você deseja usar itens de shell no -execcomando, é necessário executá-lo explicitamente em um shell, como -exec sh -c 'ffmpeg ... && rm'.

No entanto, você não deve adicionar o {} dentro do comando bash, ele produzirá problemas quando houver caracteres especiais. Em vez disso, você pode passar parâmetros adicionais para o shell depois -c command_string(consulte man sh):

$ ls
$(echo damn.)
$ find * -exec sh -c 'echo "{}"' \;
damn.
$ find * -exec sh -c 'echo "$1"' - {} \;
$(echo damn.)

Você vê que a $coisa é avaliada pelo shell no primeiro exemplo. Imagine que havia um arquivo chamado $(rm -rf /):-)

(Observação: -não é necessária, mas a primeira variável após o comando ser atribuída à variável $0, que é uma variável especial que normalmente contém o nome do programa que está sendo executado e a configuração para um parâmetro é um pouco impura, embora tenha vencido provavelmente não causará nenhum dano aqui, portanto, definimos isso apenas para -começar $1.)

Portanto, seu comando pode ser algo como

find -exec bash -c 'ffmpeg -i "$1" -sameq "$1".mp3 && rm "$1".mp3' - {} \;

Mas há uma maneira melhor. encontre suportes ande or, para que você possa fazer coisas assim find -name foo -or -name bar. Mas isso também funciona com -exec, que avalia como true se o comando sair com êxito e false se não. Veja este exemplo:

$ ls
false  true
$ find * -exec {} \; -and -print
true

Ele somente executa a impressão se o comando foi bem-sucedido, o que foi feito, truemas não para false.

Portanto, você pode usar duas instruções exec encadeadas com an -ande ela somente executará a segunda se a primeira tiver sido executada com êxito.

Marian
fonte
3
A chave parece ser a linha de Marian de "o; é um argumento por si só", que foi o que fez por mim, em termos de lâmpada e pelo meu exemplo de código. obrigado.
pjammer
3
Essa é a melhor descrição que li no -exec. É extremamente poderoso, mas sempre acho difícil obter a sintaxe correta. Isso deixou algumas coisas muito mais claras. Embrulhando particularmente o comando no shell separado. Agradável. Obrigado.
Eurospoofer 27/01
3
Observe que -ande -ornão são portáteis. O POSIX especifica -ae-o , e de fato -aé sempre assumido (portanto, não é necessário).
gniourf_gniourf
Também deve haver um espaço antes do terminador. Ex "\;"não funciona, mas " \;"faz
frmdstryr
56

Eu descobri agora. Quando você precisa executar dois comandos no exec em uma descoberta, é necessário ter dois execs separados. Isso finalmente funcionou para mim.

find . -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 \; -exec rm {} \;
Abdômen
fonte
Não tem certeza se a variável do arquivo será excluída? Alguém sabe se é esse o caso?
Abs
7
Para testar essas coisas, adicione um echoantes dos comandos e veja o que ele faz.
Marian
2
@ Marian, echoé bastante confiável - você não pode dizer a diferença entre echo "one argument"e echo one argument(a saída deste último é falsa, pois na verdade está passando echodois argumentos completamente separados). É muito melhor fazer algo assim -exec bash -c 'printf "%q " "$@"' _ ffmpeg -i {} -sameq {}.mp3 \; -printf '\n', que imprimirá a saída de maneira a tornar os caracteres não imprimíveis visíveis, para que você possa detectar novas linhas do DOS e outras curiosidades.
Charles Duffy
52

Tente colocar um espaço antes de cada \;

Trabalho:

find . -name "*.log" -exec echo {} \;

Não funciona:

find . -name "*.log" -exec echo {}\;
Dustin Cowles
fonte
1
Isso me tropeça com bastante frequência. Um dia desses, adicionarei um espaço por padrão.
harperville
Para mim, funciona exatamente o oposto no Cygwin no Windows 7: sem espaço antes \; funciona, com espaço - não. Mas se eu remover \, com espaço antes; funciona, e sem espaço antes; não, exatamente como descrito aqui.
WebComer 10/02
7

Apenas para sua informação:
Eu apenas tentei usar o comando "find -exec" em um sistema Cygwin (emulado UNIX no Windows) e parece que a barra invertida antes do ponto e vírgula deve ser removida:
find ./ -name "blabla" -exec wc -l {} ;

Dominique
fonte
Eu realmente confuso. find /etc/nginx -name '*.conf' -exec echo {} ;e find /etc/nginx -name '*.conf' -exec echo {}\;deu o mesmo resultado. :(
Kirby
Estou executando o shell Bash que acompanha o Git for Windows, e a resposta de Dustin Cowles funciona para mim. Em outras palavras: sem aspas, barra invertida escapada por ponto e vírgula, espaço após {}.
mamacdon
Não há diferença de significado, mas em alguns casos você precisa colocar a barra invertida, em alguns casos não pode colocá-la e, novamente, em outros casos, você pode escolher.
Dominique
2

Caso alguém veja "args -exec args" semelhantes nos scripts bash do Amazon Opsworks Chef, eu precisava adicionar outra barra invertida para escapar do \;

bash 'remove_wars' do
  user 'ubuntu'
  cwd '/'
  code <<-EOH
    find /home/ubuntu/wars -type f -name "*.war" -exec rm {} \\;
  EOH
  ignore_failure true
end
John Cooper
fonte
2

Além disso, se alguém tiver o argumento "find: missing to -exec", isso pode ajudar:

Em algumas conchas, você não precisa fazer a fuga, ou seja, não precisa do "\" na frente do ";".

find <file path> -name "myFile.*" -exec rm - f {} ;
sellmaurer
fonte
2
O ;não ser citado ou escapado aqui significa que pode ser consumido pelo shell, e não lido por ele find. Além disso, -fe - fsão duas coisas diferentes.
Charles Duffy
1

Você precisa escapar, eu acho.

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} \-sameq {}.mp3 \&\& rm {}\;
Matthew Smith
fonte
0

{} E && causarão problemas devido à expansão da linha de comando. Eu sugeriria tentar:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i \{} -sameq \{}.mp3 \; -exec rm \{} \;
VeeArr
fonte
Se o primeiro comando em exec for bem-sucedido e o segundo comando em exec for executado, ele ainda terá acesso à {}variável para excluir o arquivo correto?
Abs
Para o que você thinik se {}expande? A menos que contenha dois pontos ou vírgulas que permanecerão como estão.
Marian
0

Você tem que colocar um espaço entre {}e\;

Portanto, o comando será como:

find /home/me/download/ -type f -name "*.rm" -exec ffmpeg -i {} -sameq {}.mp3 && rm {} \;
Azafo Cossa
fonte
-4

Se você ainda está recebendo "find: missing argument to -exec", tente agrupar o argumento execute entre aspas.

find <file path> -type f -exec "chmod 664 {} \;"
cortejar
fonte