Jak w skrypcie powłoki mogę (1) uruchomić polecenie w tle (2) czekać x sekund (3) uruchomić drugie polecenie, gdy to polecenie jest uruchomione?

11

Oto, co muszę zrobić:

  1. rozpocznij proces A w tle
  2. poczekaj x sekund
  3. rozpocznij proces B na pierwszym planie

Jak mogę sprawić, że zacznie się czekać?

Widzę, że „sen” wydaje się zatrzymywać wszystko i nie chcę tak naprawdę „czekać”, aż proces A zakończy się całkowicie. Widziałem pewne pętle oparte na czasie, ale zastanawiam się, czy jest coś czystszego.

Julie
źródło
4
Sugeruję ulepszenie tego pytania, podając prosty przykład tego, co już wypróbowałeś.
Andy Dalton,
10
Gdzie masz wrażenie, że sleepzatrzymuje proces A? Czy możesz pokazać proces testowy, którego używasz, lub wynik, który to wskazuje? Jeśli proces-A jest zatrzymanie, to bardziej prawdopodobne, że próbuje odczytać z terminala podczas pracy w tle i coraz zatrzymany z tego powodu, raczej niż cokolwiek związanego sleep.
Charles Duffy,
3
... czy to jest przypadek, process_a </dev/null &dołączy do jego stdin /dev/nullzamiast TTY, a które mogą być wystarczające, aby uniknąć tego problemu.
Charles Duffy
Z mojego doświadczenia wynika, że ​​sen blokuje tylko bieżący proces, a zatem nie proces wcześniej rozpoczęty w tle z &
MADforFUNandHappy

Odpowiedzi:

28

O ile nie rozumiem twojego pytania, można to po prostu osiągnąć za pomocą tego krótkiego skryptu:

#!/bin/bash

process_a &
sleep x
process_b

(i dodaj dodatkową waitna końcu, jeśli chcesz, aby skrypt czekał na process_azakończenie przed zakończeniem).

Możesz to zrobić nawet jako jeden wiersz, bez potrzeby używania skryptu (jak sugeruje @BaardKopperud):

process_a & sleep x ; process_b
dr_
źródło
6
Zauważ, że nie potrzebujesz bashtego, jakakolwiek powłoka zrobi to włączając twój system sh, więc nie musisz dodawać zależności od bash dla twojego skryptu.
Stéphane Chazelas,
4
Lub po prostu: process_a & sleep x; process_b
Baard Kopperud
9

Możesz użyć operatora sterowania w tle (&), aby uruchomić proces w tle, i sleeppolecenia, aby poczekać przed uruchomieniem drugiego procesu, tj .:

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

command1 &
sleep x
command2

Oto przykład dwóch poleceń, które drukują niektóre wiadomości ze znacznikiem czasu:

#!/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

Uruchom go, aby sprawdzić, czy wykazuje pożądane zachowanie:

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
źródło
2
Pytanie dotyczy „rozpoczęcia procesu B na pierwszym planie ”.
Sparhawk
1
@Sparhawk Wow. Całkowicie źle to przeczytałem - bez wyjaśnienia. Dzięki za heads-up - poprawiony kod. Poważna nostalgia od twojej nazwy użytkownika, btw.
igal
1
Bez obaw! (Również nazwa użytkownika była pierwotnie odniesieniem do tego faceta , ale dopiero później zdałem sobie sprawę, że była to postać z Eddings! I sprawia, że ​​chcę ponownie przeczytać te książki…)
Sparhawk
5

Podoba mi się odpowiedź @ dr01, ale on nie sprawdza kodu wyjścia, więc nie wiesz, czy ci się udało, czy nie.

Oto rozwiązanie, które sprawdza kody wyjścia.

#!/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}

zwykle przechowuję PID w tablicy bash, a następnie sprawdzanie pid jest pętlą for.

Trevor Boyd Smith
źródło
2
Możesz odzwierciedlić te kody wyjścia w stanie wyjścia skryptu, na przykładA & sleep x; B; ret=$?; wait "$!" || exit "$ret"
Stéphane Chazelas
1
@ StéphaneChazelas dobry pomysł. zaktualizowałem kod, aby upewnić się, że w przypadku niepowodzenia jednego lub obu wystąpi niezerowy kod wyjścia.
Trevor Boyd Smith
OP chce process_bdziałać na pierwszym planie. Prawdopodobnie process_amożna wyjść, gdy process_bjest uruchomiony, ale nadal możesz waitto zrobić i uzyskać status wyjścia po zebraniu statusu wyjścia na pierwszym planie process_bw normalny sposób, $?bezpośrednio.
Peter Cordes,