Nele man bash
diz:
espere [-fn] [id...]
Aguarde cada processo filho especificado e retorne seu status de encerramento. Cada ID pode ser um ID de processo ou uma especificação de trabalho; se uma especificação de trabalho for fornecida, todos os processos no pipeline desse trabalho serão aguardados. Se id não for fornecido, todos os processos filhos ativos no momento serão aguardados e o status de retorno será zero. Se a opção -n for fornecida, wait aguarda o término de qualquer tarefa e retorna seu status de saída.
E exemplos típicos como
command1 & command2 & command3 & wait
significará que os três comandos serão executados em paralelo e o próximo passo só será feito quando TODOS eles estiverem concluídos.
Meu problema está nos resultados desses dois scripts bash:
#!/bin/bash
for i in 1 2 3 ; do
a=$i
echo "$a is $i"
done 2>/dev/null
Resultado:
1 is 1
2 is 2
3 is 3
Praticamente o que eu esperava. Agora suponho que atribuir a variável seja um processo longo, então espero por isso:
#!/bin/bash
for i in 1 2 3 ; do
a=$i & wait
echo "$a is $i"
done 2>/dev/null
Resultado é:
3 is 1
3 is 2
3 is 3
e estou bastante confuso pelas seguintes razões:
- O único processo
wait
que deve esperar para terminar é atribuir a variável, então a próxima etapa do script deve ser executada (echo
).a=3
deve acontecer apenas na última iteração do loop. - Até onde eu sei,
for
-loops são executados em subshells ewait
tem um escopo apenas para o shell em que foi iniciado. Portanto, não deve nem esperar que ofor
-loop termine (já que este é o pai). - Em nenhum momento eu especifiquei
echo
para correr em paralelo com algum outro processo, então não esperava uma condição de corrida.
Então, por a
que a variável -variável é definida com a última iteração do loop quando a variável $i
não é? Que parte do wait
comando eu não entendi? O comportamento está completamente fora das minhas expectativas.
GNU bash, versão 5.0.3(1) em um kernel Linux 5.7.0-0.bpo.2-amd64.
Desarmar a
antecipadamente faz com que o segundo script retorne isso
is 1
is 2
is 3
ou seja, a variável nunca é definida e foi arrastada da minha execução anterior.
for
loops não são executados em um subshell, por exemplo, você pode terfor i in 1 2 3; do a=foo; done; echo $a
e imprimiráfoo
, mesmo que a variável seja usada fora do loop. O valor da variável de loop também é o que foi atribuído pela última vez pelo loop (que pode não ser o último valor, se o loop foibreak
retirado).Mas usar
&
coloca esse comando em um subshell, e fazera=$i &
faz com que a atribuição aconteça apenas no subshell. Você também pode usar o valor atribuído no subshell:Por exemplo:
imprimiria
fazer algo como
{ a=$i; echo "$a"; } &
verificar.Ter os comandos em segundo plano acessando (e modificando!) variáveis no shell principal exigiria alguma sincronização entre os processos, e isso traria complexidade ao shell, provavelmente para um ganho relativamente pequeno.
Se você precisar , por exemplo, se você tiver um comando de longa duração que deseja executar em segundo plano, mas precisar da saída, terá que fazer algo como armazenar a saída temporariamente em um arquivo, por exemplo
Ou use algo como o GNU Parallel, desenvolvido especificamente para executar comandos em paralelo. Ele também tem o
parset
comando que deve ser capaz de atribuir valores a uma variável do shell.Quanto ao motivo de você ter saído
3
de$a
cada iteração do loop, provavelmente é porque você executou o primeiro loop antes e deixoua
para3
. Com todas as atribuições subsequentes inseridas no subshell, esse é o valor que permaneceua
para o shell principal, para todo o loop.