Em um script de shell, como posso (1) iniciar um comando em segundo plano (2) esperar x segundos (3) executar um segundo comando enquanto esse comando estiver em execução?

11

É isso que eu preciso que aconteça:

  1. iniciar o processo A em segundo plano
  2. aguarde x segundos
  3. iniciar o processo B em primeiro plano

Como posso fazer a espera acontecer?

Estou vendo que "dormir" parece interromper tudo e, na verdade, não quero "esperar" pelo processo A terminar completamente. Eu já vi alguns loops baseados no tempo, mas estou me perguntando se há algo mais limpo.

Julie
fonte
4
Sugiro que você aprimore essa pergunta, fornecendo um exemplo simples do que você já tentou.
Andy Dalton
10
Onde você tem a impressão de que sleepinterrompe o processo A? Você pode mostrar o processo de teste que você está usando ou produzir um indicativo disso? Se o processo A estiver sendo interrompido, é mais provável que ele esteja tentando ler a partir do terminal enquanto estiver executando em segundo plano e for interrompido por esse motivo, em vez de algo relacionado a sleep.
Charles Duffy
3
... se esse é o caso, process_a </dev/null &irá anexar sua stdin a /dev/nullvez do TTY, e que pode ser suficiente para evitar o problema.
Charles Duffy
Da minha experiência de sono só irá bloquear o processo atual e, portanto, não o processo anteriormente iniciado em segundo plano com &
MADforFUNandHappy

Respostas:

28

A menos que eu esteja entendendo mal sua pergunta, ela pode simplesmente ser alcançada com este pequeno script:

#!/bin/bash

process_a &
sleep x
process_b

(e adicione um acréscimo waitno final, se você quiser que seu script espere para process_aterminar antes de sair).

Você pode até fazer isso como uma linha, sem a necessidade de um script (como sugerido por @BaardKopperud):

process_a & sleep x ; process_b
dr01
fonte
6
Observe que você não precisa bashdisso, pois qualquer shell incluirá o sistema sh, portanto, você não precisa adicionar uma dependência no bash para o seu script.
Stéphane Chazelas
4
Ou simplesmente: process_a & sleep x; process_b
Baard Kopperud 2/17/17
9

Você pode usar o operador de controle em segundo plano (&) para executar um processo em segundo plano e o sleepcomando para aguardar antes de executar um segundo processo, ou seja:

#!/usr/bin/env bash
# script.sh

command1 &
sleep x
command2

Aqui está um exemplo de dois comandos que imprimem algumas mensagens com registro de data e hora:

#!/usr/bin/env bash

# Execute a process in the background
echo "$(date) - Running first process in the background..."
for i in {1..1000}; do
    echo "$(date) - I am running in the background";
    sleep 1;
done &> background-process-output.txt &

# Wait for 5 seconds
echo "$(date) - Sleeping..."
sleep 5 

# Execute a second process in the foreground
echo "$(date) - Running second process in the foreground..."
for i in {1..1000}; do
    echo "$(date) - I am running in the foreground";
    sleep 1;
done

Execute-o para verificar se ele exibe o comportamento desejado:

user@host:~$ bash script.sh

Fri Dec  1 13:41:10 CST 2017 - Running first process in the background...
Fri Dec  1 13:41:10 CST 2017 - Sleeping...
Fri Dec  1 13:41:15 CST 2017 - Running second process in the foreground...
Fri Dec  1 13:41:15 CST 2017 - I am running in the foreground
Fri Dec  1 13:41:16 CST 2017 - I am running in the foreground
Fri Dec  1 13:41:17 CST 2017 - I am running in the foreground
Fri Dec  1 13:41:18 CST 2017 - I am running in the foreground
Fri Dec  1 13:41:19 CST 2017 - I am running in the foreground
Fri Dec  1 13:41:20 CST 2017 - I am running in the foreground
...
...
...
igal
fonte
2
A pergunta pede para "iniciar o processo B em primeiro plano ".
Sparhawk
1
@Sparhawk Wow. Totalmente mal interpretado isso - nenhuma explicação. Obrigado pelo alerta - código corrigido. Nostalgia séria do seu nome de usuário, btw.
Igal
1
Não se preocupe! (Além disso, o nome de usuário era originalmente uma referência a esse cara , mas eu só mais tarde percebi que era um caráter Eddings E me faz querer voltar a ler esses livros ...!)
Sparhawk
5

Eu gosto da resposta do @ dr01, mas ele não verifica o código de saída e, portanto, você não sabe se teve sucesso ou não.

Aqui está uma solução que verifica os códigos de saída.

#!/bin/bash

# run processes
process_a &
PID1=$!
sleep x
process_b &
PID2=$!
exitcode=0

# check the exitcode for process A
wait $PID1    
if (($? != 0)); then
    echo "ERROR: process_a exited with non-zero exitcode" >&2
    exitcode=$((exitcode+1))
fi

# check the exitcode for process B
wait $PID2
if (($? != 0)); then
    echo "ERROR: process_b exited with non-zero exitcode" >&2
    exitcode=$((exitcode+1))
fi
exit ${exitcode}

Normalmente, eu armazeno os PIDs em uma matriz do bash e a verificação do pid é um loop for.

Trevor Boyd Smith
fonte
2
Você pode querer refletir essas códigos de saída no estado de saída do seu script comoA & sleep x; B; ret=$?; wait "$!" || exit "$ret"
Stéphane Chazelas
1
@ StéphaneChazelas boa ideia. Atualizei o código para garantir que um código de saída diferente de zero seja retornado se um ou ambos falharem.
Trevor Boyd Smith
O OP quer process_brodar em primeiro plano. Presumivelmente, process_apode sair enquanto process_bestiver em execução, mas você ainda pode waitobtê-lo e obter o status de saída depois de coletar o status de saída do primeiro plano process_bda maneira normal, $?diretamente.
Peter Cordes