Tente usar a opção -u do GNU paralelo.
echo "1\n2\n3" | parallel -u -IX ./handle_X.sh
Isso os executa em paralelo, sem armazenar todo o processo.
Eu tenho três tipos de dados que estão em diferentes formatos; para cada tipo de dados, existe um script Python que o transforma em um único formato unificado.
Este script Python é lento e limitado pela CPU (para um único núcleo em uma máquina com vários núcleos), portanto, quero executar três instâncias dele - uma para cada tipo de dados - e combinar sua saída para passá-la para% código%. Basicamente, equivalente a isto:
{ ./handle_1.py; ./handle_2.py; ./handle_3.py } | sort -n
Mas com os três scripts sendo executados em paralelo.
Eu encontrei esta questão onde o GNU sort
estava sendo usado para round-robin algum fluxo stdout entre n instâncias de um script que lida com o fluxo.
A partir da página do manual de divisão:
-n, --number=CHUNKS
generate CHUNKS output files. See below
CHUNKS may be:
N split into N files based on size of input
K/N output Kth of N to stdout
l/N split into N files without splitting lines
l/K/N output Kth of N to stdout without splitting lines
r/N like 'l' but use round robin distributio
Portanto, o comando split
implica em " sem separar linhas ".
Com base nisso, parece que a solução a seguir deve ser viável:
split -n r/3 -u --filter="./choose_script" << EOF
> 1
> 2
> 3
> EOF
Onde r/N
faz isso:
#!/bin/bash
{ read x; ./handle_$x.py; }
Infelizmente, vejo algumas misturas de linhas - e muitas linhas novas que não deveriam estar lá.
Por exemplo, se eu substituir meus scripts Python por alguns scripts bash simples que fazem isso:
#!/bin/bash
# ./handle_1.sh
while true; echo "1-$RANDOM"; done;
.
#!/bin/bash
# ./handle_2.sh
while true; echo "2-$RANDOM"; done;
.
#!/bin/bash
# ./handle_3.sh
while true; echo "3-$RANDOM"; done;
Eu vejo esta saída:
1-8394
2-11238
2-22757
1-723
2-6669
3-3690
2-892
2-312511-24152
2-9317
3-5981
Isso é irritante - com base no extrato da página man que colei acima, ele deve manter a integridade da linha.
Obviamente, funciona se eu remover o argumento choose_script
, mas ele está em buffer e eu ficarei sem memória, pois ele armazena a saída de todos os scripts, exceto um.
Se alguém tiver alguma idéia aqui, seria muito apreciado. Estou fora da minha profundidade aqui.
Tente usar a opção -u do GNU paralelo.
echo "1\n2\n3" | parallel -u -IX ./handle_X.sh
Isso os executa em paralelo, sem armazenar todo o processo.
Tente:
parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py
Se handle_1.py
tiver um nome de arquivo:
parallel ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*
Você não deseja a saída misturada, portanto, não use -u.
Se você quiser manter o pedido (portanto, toda a saída do handle_1 é anterior ao handle_2 e, portanto, você pode evitar a classificação):
parallel -k ::: ./handle_1.py ./handle_2.py ./handle_3.py ::: files*
Se você ainda quiser classificá-lo, é possível fazer o paralelismo da classificação e utilizar sort -m
:
parallel --files "./handle_{1}.py {2} | sort -n" ::: 1 2 3 ::: files* | parallel -j1 -X sort -m
Defina $ TMPDIR como um diretório que seja grande o suficiente para conter a saída.
Talvez eu esteja sentindo falta de algo, mas você não pode fazer isso:
(./handle_1.py & ./handle_2.py & ./handle_3.py) | sort -n
Se você quiser que as linhas de cada processo não sejam intercaladas, é mais fácil certificar-se de que o próprio processo as grave completamente e, possivelmente, desabilite o buffer de saída, já que write
s para um pipe tem a garantia de ser atômico. eles não são maiores que PIPE_BUF
. Por exemplo, você pode ter certeza de que faz usar buffer de saída à lastdio
e chamar fflush
ou qualquer equivalente em python
depois de uma ou algumas linhas terem sido gravadas.
Se você não pode modificar os scripts python, você pode fazer:
lb() { grep --line-buffered '^'; }
(com o GNU grep) ou:
lb() while IFS= read -r l; do printf '%s\n' "$l"; done
(Veja notas nos comentários abaixo se a saída dos comandos não é texto)
E faça:
(./handle_1.py | lb & ./handle_2.py | lb & ./handle_3.py | lb) | sort -n
Outra opção para evitar esses processos de 3 lb
é ter três canais para um comando que usa select
/ poll
para ver de onde vem alguma saída e alimentá-la para sort
com base em linha, mas é preciso um pouco de programação.
A resposta da Flowbok foi a solução correta. Estranhamente, a saída do GNU parallel
fica desconfigurada se for enviada diretamente para um arquivo - mas não se for para um tty.
Felizmente, script -c
está disponível para imitar um tty.
Ainda existem três scripts:
#!/bin/bash
# handle_1.sh
while true; do echo "1-$RANDOM$RANDOM$RANDOM$RANDOM"; done
.
#!/bin/bash
# handle_2.sh
while true; do echo "2-$RANDOM$RANDOM$RANDOM$RANDOM"; done
.
#!/bin/bash
# handle_3.sh
while true; do echo "3-$RANDOM$RANDOM$RANDOM$RANDOM"; done
Depois, há um arquivo que encapsula a chamada para o paralelo:
#!/bin/bash
# run_parallel.sh
parallel -u -I N ./handle_N.sh ::: "1" "2" "3"
E então eu chamo assim:
script -c ./run_parallel > output
As linhas na saída são misturadas linha por linha entre a saída dos diferentes scripts, mas elas não são confusas ou intercaladas em uma determinada linha.
Comportamento bizarro de parallel
- posso enviar um relatório de bug.