encontre -exec no script bash com expansão variável

14

Estou tentando executar um comando semelhante ao abaixo em um script bash. Ele deve procurar por todas as subpastas $sourcedire copiar todos os arquivos de um determinado tipo para o nível raiz de $targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

Isso parece bem próximo, exceto que {}não está sendo repassado $2para-exec sh -c ...

Eu gostaria de fazer isso o mais próximo possível da "maneira correta", com tolerância a caracteres especiais em nomes de arquivos (especificamente aspas simples).

Editar: vejo pessoas sugerindo o uso xargsou encadeamento de argumentos. Fiquei com a impressão de que isso não é aceitável para um número limitado de argumentos. Se eu tiver, por exemplo, milhares de arquivos .jpg que estou tentando copiar de vários diretórios da galeria para um diretório gigante de apresentações de slides, as soluções que encadearão argumentos ainda funcionarão?

Edit 2: Meu problema era que faltava um _antes da minha primeira opção para sh no -execcomando. Para quem estiver curioso sobre como fazer o comando find funcionar, adicione _oe tudo ficará bem:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

Aceitei uma resposta abaixo, porém, porque ela realiza a mesma tarefa, mas é mais eficiente e elegante.

Mateus
fonte
4
Foi exatamente por isso que xargsfoi criado, para lidar automaticamente com grandes quantidades de argumentos em comandos regulares que tinham limites. Além disso, a maioria dos limites máximos de argumentos foi amplamente aprimorada para os utilitários GNU padrão. Você também verá um benefício no desempenho, evitando todos os garfos do processo, que em milhares de arquivos são relevantes.
JM Becker
Com o gnu-find e + em vez de ";", você também pode lidar com vários argumentos ao mesmo tempo com o find. E você salva o argumento complicado que passa com -print0.
usuário desconhecido
@userunknown: Estou respondendo a isso abaixo da sua resposta.
JM Becker
@ usuário desconhecido Bem, eu amo esse código. É pelo menos totalmente compatível com POSIX e funcionará sem nenhum material GNU na máquina. Há aqueles momentos em que você não precisa disso, especialmente em servidores no trabalho.
Syntaxerror 14/12

Respostas:

6

Deseja copiar arquivos de um tipo específico, para um diretório específico? É melhor fazer isso xargs, e você nem precisa disso sh. Essa é uma maneira mais apropriada de fazê-lo, também deve ser executada com mais eficiência.

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

Se você precisar manipular nomes de arquivos especiais, use NULLcomo seu separador

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"
JM Becker
fonte
1
Para o 2º caso, não se esqueça de adicionar -print0a finde -0axargs
SiegeX
@ SiegeX, já estava fazendo isso, antes de eu perceber seu comentário.
JM Becker
Além disso, NULLnão é necessário se você usar '{}', é importante quando não o fizer. O benefício real entre um dos outros, além da POSIXconformidade, é o desempenho.
JM Becker
6

Você precisa passar {}como argumento para o shell e fazer um loop sobre cada argumento.

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

Nota : A maneira como isso funciona é que o primeiro argumento para um shell é o nome do shell . Podemos explorar isso passando o nome como o $targetdire, em seguida, usar o parâmetro especial $0dentro do script do shell para acessar o targetdir.

SiegeX
fonte
1
"$targetdir"não é expandido entre aspas simples.
enzotib 02/02
5

Se você não acredita na igreja de xargs:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

Explicação:

cp -t a b c d 

copia b, c para o dir de destino a.

-exec cmd {} +

chama o comando em um grande grupo de arquivos de uma só vez, não um após o outro (que é padrão, se você usar em ";"vez de +). É por isso que é necessário puxar o targetdir para frente e marcá-lo explicitamente como alvo.

Isso funciona para o gnu-find e pode não ser para outras implementações do find. Claro que também depende da bandeira -t-bandeira.

É claro que o TechZilla está certo até agora, pois shnão é necessário para invocar o cp.

Se você não usar xargs, o que geralmente é desnecessário em combinação com find, será salvo do aprendizado da sinalização -print0e -0.

Usuário desconhecido
fonte
1
Obviamente, qual método alguém pode preferir é algo subjetivo. Com isso dito, existem razões para continuar usando xargs. Maior exemplo, e se você não estiver encontrando arquivos? Eu uso xargs no lugar de forloops gerais o tempo todo, findé muito menor em escopo. Além disso, findapenas suporta que, +se você estiver usando o GNU find, ele não está definido no POSIX. Portanto, embora você sinta-se livre para preferir findsozinho, ele não faz tudo que o xargs faz. Quando você considera o GNU, xargstambém obtém o -Pque é multi-core. Vale a pena aprender xargsindependentemente.
JM Becker
Subjetividade falando, minha opinião obviamente difere. Objetivamente, por outro lado, sua resposta também está correta. É uma das poucas melhores soluções disponíveis.
JM Becker
@TechZilla: Espero lembrar de revisitar este site, quando o find começar a oferecer suporte à invocação paralela. :) Na maioria dos casos, com cópia / movimento, a velocidade do disco será o fator limitante, mas os SSDs podem mudar a imagem. Você está certo, que uma solução não-GNU a fornecer é uma coisa boa. Senão, a solução de localização é objetivamente mais curta, mais simples e usa apenas dois processos.
usuário desconhecido
0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;
Berthad33
fonte
3
Considere adicionar algumas explicações para sua solução.
precisa saber é o seguinte