find /source/dir/ -type f -name '*.avi' -exec sh -c 'f="{}"; test ! -f "${f%.*}.idx" && cp "$f" /target/dir/' \;
Este comando encontra todos os arquivos .avi
sob o /source/dir/
e gera um shell básico para cada um. O shell então testa se não há um arquivo .idx
correspondente e invoca cp
quando necessário.
Edit: Eu mudei o comando um pouco para torná-lo mais infalível. Após a solicitação explícita, explico o comando inteiro com mais detalhes.
-
find
- Encontre ... -
/source/dir/
- ... em um determinado diretório ... -
-type f
- ... todos os arquivos ... -
-name '*.avi'
- ... com nomes correspondentes a este padrão ... -
-exec
- ... e para cada arquivo encontrado, execute ... -
sh ...
- ... um shell com seus próprios parâmetros (explicado posteriormente). -
\;
- indica onde o-exec
termina. O truque importante é: cada string{}
é substituída pelo caminho real para o arquivo específico encontrado; é assim quefind -exec
funciona.
Cada shell recebe a opção -c
, que lhe diz para executar o próximo argumento como se fosse um comando digitado. O shell vê esse comando com {}
já substituído pelo caminho do arquivo, conforme explicado acima.
E o comando é assim:
-
f="{}"
- Memorize o caminho sob o nomef
(lembre-se: neste momento já existe algum caminho em vez de{}
). -
;
- separador de comandos. -
test
- Teste e retorne o status de saída de acordo; os argumentos a seguir dirão o que testar.-
!
- Cuidado: o teste está invertido. -
-f
- Teste se há um arquivo existente ... -
"${f%.*}.idx"
- ... sob este caminho. A construção${f%.*}
informa ao shell para obter o conteúdo da variávelf
sem o fragmento do último ponto.
para o final. Eu adiciono outra extensão. Desta forma eu os troco.
-
-
&&
- Apenas se o comando anterior (ou seja,test
) retornou o status de saída0
... -
cp
- ... copie ...-
"$f"
- ... o arquivo que foi encontrado porfind
... -
/target/dir/
- ... para o diretório de destino.
-
Notas finais:
- Eu preciso de um shell para cuidar de
"${f%.*}.idx"
e&&
. Se eu quisesse copiar todos os arquivos encontrados, então... -exec cp "{}" /target/dir/ \;
seria suficiente (e ainda assim longe do ideal, mas não se distraia). - É bom citar (como em
""
) todos os caminhos que podem incluir espaços, caracteres especiais, etc. - O argumento (comando) para
sh
está entre aspas simples (''
). Se if foi colocado entre aspas duplas (""
), a expansão de"${f%.*}.idx"
ocorreria no shell atual (aquele que executafind
) e isso não é o que eu quero. Você pode ler mais aqui .