Como executar não mais do que n subcamadas paralelas

3

Estou tentando executar subscritos a partir de um script principal, mas quero ter certeza de que não mais que n subscritos sejam executados ao mesmo tempo.

O seguinte exemplo simplificado ilustra.

Cada índice cria um arquivo fictício na RAM ( /dev/shm/ ) com um nome feito de um registro de data e hora exclusivo e o exclui uma vez concluído.

O script principal conta o número de arquivos fictícios em /dev/shm/ originados dos subscritos, e não (não deve) lançar um novo subscrito se 2 ou mais deles já estiverem em execução.

No entanto, o script principal parece ignorar a condição while e inicia todos os 5 subscritos de uma só vez.

O que há de errado com meu código?

mainscript.txt

#!/bin/bash
for counter in $(seq 1 5)
do
        while [ $(ls -1 /dev/shm/|grep "script044"|wc -l) -ge 2 ]
        do
                sleep 0
        done

        xterm -e "bash script044.txt" &
done

exit

script044.txt (subscrito)

#!/bin/bash

tempfilename="script044_"$(date +%Y%m%d%H%M%S)_${RANDOM}
echo > /dev/shm/${tempfilename}

for counter in $(seq 1 $(shuf -i 10-45 -n 1))
do
        sleep 1
        printf "${counter}\r"
done

rm /dev/shm/${tempfilename}

exit
    
por Sébastien Clément 31.07.2015 / 17:21

6 respostas

3

(Convenção - .txt são apenas arquivos de texto simples.% arquivos.sh são arquivos de script de shell.).

Seu script mainscript.txt tem uma condição de corrida. Especificamente, o loop while inicia sua próxima iteração antes que o script script044.txt seja capaz de criar o arquivo temporário. Na verdade, todo o loop é iterado antes de qualquer um desses arquivos ser criado.

Uma maneira mais robusta de lidar com esse tipo de coisa é esquecer os arquivos temporários e usar o shell incorporado em wait :

#!/bin/bash

pid_count=0
for counter in $(seq 1 5)
do
    xterm -e "bash script044.txt" &
    if (( ++pid_count > 2 )); then
        wait -n
        ((pid_count--))
    fi
done

Isso incrementa um contador toda vez que um subprocesso é iniciado. Se o contador for maior que 3, então wait para o próximo subprocesso terminar. Quando wait retorna, então decrementamos o contador e contornamos novamente para iniciar o próximo xterm.

Você pode remover todas as linhas relacionadas a tempfilename do script044.txt - elas não são mais necessárias.

Como aponta @chepner, a opção necessária -n está disponível apenas no bash 4.3 ou posterior.

    
por 31.07.2015 / 18:32
2

Embora não ajude se você está buscando uma solução somente de shell, o GNU paralelo fornece um sem comando que pode ajudar nessa situação exata.

O seguinte (não testado desde que eu não tenho o seu script) deve executar o seu trabalho 5 vezes, mas apenas 2 de cada vez, esperando para sair no final:

LIMIT=2
for i in {1..5}; do
    sem -j $LIMIT 'term -e "bash script044.txt"'
done
sem --wait
    
por 31.07.2015 / 19:02
1

Parece que você quer IPC. Em vez de dar um loop no modo de suspensão e fazer um teste toda vez que você fizer isso, você pode simplesmente esperar que o processo filho avise quando estiver pronto. É para isso que servem os canos.

Você pode ter o relatório de processos filho para o pai. Abra um pipe e compartilhe-o com eles. Quando eles terminam, só precisam avisar os pais.

sub()(  trap "echo >&9" 0
        sleep 5
)
eval    "exec 9<>"<(echo);i=0
until   [ "$((i+=1))" -gt 5 ]
do      sub & read na <&9
        date +%S:%t"$i"
done

Eu abro usando a substituição do processo. Se você não pode fazer isso no seu shell, você pode usar:

mkfifo pipe; exec 9<>pipe; rm pipe; echo >&9

Agora o echo inicial coloca uma linha no pipe - nos dois casos. Isso coloca você em um processo de espera à frente desde o início - o que significa que você executará dois processos simultâneos o tempo todo. Este script usa date para informar sobre os segundos entre cada chamada de sub() . Aqui está a saída:

34: 1
39: 2
39: 3
44: 4
44: 5

Lá. Agora, como você pode ver, a cada 5 segundos um filho morre e, quando isso acontece, echo é uma linha para o canal em que o read está bloqueando no momento. Assim que read achar que é uma nova linha na entrada, pode sair do que está a fazer e começar de novo.

Você só precisa colocar um trap em seu script_044, que instrui os processos filhos a chamarem o pai quando eles passarem.

    
por 31.07.2015 / 21:29
1

Uma maneira genérica de fazer isso é usar xargs

printf "%s\n" {1..5} | xargs -P2 -n1 -i xterm -e '/bin/echo Job {}; bash'

printf está lá apenas para preencher xargs com valores. O padrão xargs replace-str é {} se o sinalizador -i for usado. Por favor, leia a manpage do xargs para obter mais informações detalhadas. Isso deve funcionar com as ferramentas GNU e bash como shell.

xargs flags: 
-P     Run up to max-procs processes at a time.[…]
-n     Use  at most max-args arguments per command line.[…]
-i/-I  Replace occurrences of replace-str in the initial-arguments with names read from standard input.[…]
    
por 01.08.2015 / 02:36
0

Insira algum atraso logo após o lançamento de uma nova instância de xterm ...

        xterm -e "bash script044.txt" &
        sleep 0.1
    
por 31.07.2015 / 18:26
0

Usando o GNU Parallel, é assim:

seq 1 5 | parallel -j2 -N0 'xterm -e "bash script044.txt"'

O GNU Parallel é um paralelizador geral e facilita a execução de trabalhos em paralelo na mesma máquina ou em várias máquinas para as quais você tem acesso ssh. Muitas vezes, pode substituir um loop for .

Se você tem 32 tarefas diferentes que você quer rodar em 4 CPUs, uma forma direta de paralelizar é rodar 8 tarefas em cada processador:

O

GNUParallelgeraumnovoprocessoquandoumtermina-mantendoasCPUsativaseeconomizandotempo:

Instalação

Se o GNU Parallel não for empacotado para sua distribuição, você poderá fazer uma instalação pessoal, que não requer acesso root. Isso pode ser feito em 10 segundos ao fazer isso:

(wget -O - pi.dk/3 || curl pi.dk/3/ || fetch -o - http://pi.dk/3) | bash

Para outras opções de instalação, consulte o link

Saiba mais

Veja mais exemplos: link

Assista aos vídeos de introdução: link

Percorra o tutorial: link

Inscreva-se na lista de e-mail para obter suporte: link

    
por 01.08.2015 / 06:33