Use para loop em encontrar exec

6
 -> find  .. -name bin  -exec  for file in {}/* ; do echo $file ; done  \;

   -bash: syntax error near unexpected token 'do'

Qual é a sintaxe apropriada para este comando?

    
por Prospero 01.04.2015 / 19:46

3 respostas

4

Para usar várias instruções, como for -loop, como o argumento para -exec , é necessário invocar um shell, como bash , explicitamente:

find .. -name bin -exec bash -c 'for file in "$1"/* ; do echo "$file" ; done' none {}  \;

Isso é seguro até mesmo para nomes de arquivos que contenham espaços ou outros caracteres hostis.

Como o bash -c funciona

Pode-se invocar o bash com um comando do formulário:

bash -c some_complex_commands arg0 arg1 arg2 ...

Nesse caso, o bash executará o que estiver na string some_complex_commands . Esses comandos podem fazer uso dos parâmetros posicionais de shell comuns . O primeiro argumento após o comando, arg0 acima, é atribuído a $0 , o segundo a $1 , o terceiro a $2 , etc.

Quando alguém executa um script de shell normal, $0 é o nome do script e $1 é o primeiro argumento que aparece na linha de comando. De acordo com essa tradição, o comando bash -c foi escrito para atribuir o nome do arquivo, {} na notação de find, a $1 . Como esse script não tem um nome sensato, none é atribuído como um espaço reservado para $0 .

    
por 01.04.2015 / 19:57
1

Você pode aproximar a saída do seu pseudo-código com as primitivas find :

find .. -path \*/bin/\* ! -name .\* 

... que deve imprimir apenas arquivos / diretórios com nomes que não começam com a. e que estão enraizados em algum nível no diretório pai e, em maior grau, em um diretório chamado /bin .

Em geral, posso pensar em todos os tipos de propósitos práticos para loop em um processo filho find -exec , mas não em um em que eu considere prático fazer o shelld glob em um argumento passado por find . Para fazer a mesma coisa que seu pseudo-código faz com printf - porque seu uso pode garantir tradução literal de argumentos para saída - você pode fazer ...

find .. -type d -name bin -exec sh -c '
    printf %s\n "$0/"*' {} \;

... que faz a impressão e a globbing sem o loop for . No entanto, uma diferença entre este e seu comando de exemplo é que sem a especificação -type d e o tipo de resultado correspondente ao nome bin será echo d - e é muito provável que você veja muito bin/* sendo escrito para stdout. É claro que, mesmo com w / -type d , não há garantia de que * resolverá - um diretório vazio ou um contendo apenas . arquivos não renderizará correspondências e, portanto, você poderá vê-lo de qualquer maneira.

Observe também que o pseudocódigo de exemplo, porque ele usa as primitivas {} \; , pode ser muito mais lento do que algumas outras maneiras. Vamos tentar a coisa printf novamente como:

find .. -type d -name bin -exec sh -c '
    for d do printf %s\n "$d/"*; done' -- {} +

... que ainda arrisca o caso vazio, mas em vez de -exec ing um shell por correspondência, ele reúne tantos argumentos quanto poderia razoavelmente passar para outro processo em exec time e passa o lote para sh em "$@" sua matriz de parâmetros posicionais - que podemos fazer um loop com for d do printf... .

Agora, se você quiser fazer algo diferente de apenas imprimir resultados - que normalmente consideraria útil sobre looping em uma instrução -exec -, é possível retornar ao exemplo anterior de -path e combiná-lo com -exec como ...

find .. -path \*/bin/\* -exec sh -c '
    for arg do : something with "$arg"
done' -- {} +
    
por 02.04.2015 / 01:07
0

Parece que você tem coisas invertidas do que você quer. Tente isto:

for f in 'find .. -name bin'
do
echo $f
done
    
por 01.04.2015 / 19:49

Tags