Bash, jak uruchomić niektóre procesy w tle, ale poczekać na inne?

11

Mam (jeszcze) inną wait, &, &&pytanie przepływu sterowania ..

Powiedzmy, że mam skrypt taki jak ten, w którym chcę wykonać jak najwięcej pracy w tym samym czasie:

# may take some hours
something InputA > IrrelevantA &
something InputB > IrrelevantB &

# may take an hour
(
   somethingElse InputA > OutputA &
   somethingElse InputB > OutputB &
)&& combine OutputA OutputB > Result

...morestuff

Pytanie 1: Czy w skrypcie combineczeka , aż oba somethingElseprocesy zakończą się, dopóki oba somethingprocesy będą kontynuowane?

Pytanie 2: Jeśli nie - i podejrzewam, że nie - jak mogę combineczekać tylko na oba somethingElseprocesy, podczas gdy somethingpowyższe procesy nadal działają w tle?

Stephen Henderson
źródło

Odpowiedzi:

13

W twoim przykładzie combinepolecenie zostanie uruchomione zaraz po wyjściu podpowłoki (i pod warunkiem, że ostatni proces w tle został uruchomiony bez błędu). Podkładka zakończy działanie natychmiast po uruchomieniu zadań, ponieważ nie ma waitpolecenia.

Jeśli chcesz wykonać polecenie oparte na wartości zwracanej przez dwa lub więcej jednoczesnych procesów w tle, nie widzę innego sposobu niż użycie plików tymczasowych dla wartości zwracanych. Wynika to z faktu, że waitmoże zwrócić tylko zwracaną wartość jednego z procesów, na które czeka. Ponieważ procesy w tle muszą być uruchamiane w podpowłokach, aby w ogóle uzyskać wartości zwracane, nie można ich przechowywać w zmiennych. Mógłbyś:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

tmp1=$(mktemp)
tmp2=$(mktemp)

( somethingElse InputA >OutputA; echo $? >"$tmp1" ) &
proc1=$!

( somethingElse InputB >OutputB; echo $? >"$tmp2" ) &
proc2=$!

wait "$proc1" "$proc2"

read ret1 <"$tmp1"
read ret2 <"$tmp2"
[ "$ret1" = 0 && "ret2" = 0 ] && combine OutputA OutputB >Result

rm "$tmp1" "$tmp2"

Jeśli tak naprawdę nie zależy ci na wartościach zwracanych, możesz po prostu rozpocząć zadania normalnie i użyć wait:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result
Graeme
źródło
Cześć, myślę, że druga opcja zadziałałaby dla mnie ...
Stephen Henderson
3

Byłoby podstawienie proces będzie bardziej efektywny, zwłaszcza jeśli nie ma potrzeby, aby zapisać pliki OutputAi OutputB, i dbają tylko o Result? Czy byłoby to szczególnie oszczędzające czas, ponieważ jeśli masz wolne operacje we / wy w zapisywaniu na dysk, zapisywanie plików OutputAi OutputBmoże to być etap ograniczający szybkość?

combine  <(somethingElse InputA)  <(somethingElse InputB)  >  Result

Podstawianie procesów pozwala umieścić polecenie w środku <(..here..)zamiast zapisywać dane wyjściowe w pliku, a następnie odczytywać je jako dane wejściowe w kroku „łącz”.

Jeśli pamięć jest ograniczeniem, a rozmiar outputAi outputBwięcej niż pamięć może pomieścić, czy uda mu się pokonać cały cel?

Czy combinezaczekaj, aż oba procesy zostaną zakończone, zanim zacznie działać?

TW Tan
źródło
To nie jest „Jeopardy”; proszę nie wyrażać swojej odpowiedzi w formie pytania. Poważnie, wpadłeś na nowy pomysł i myślę, że jest całkiem dobry. Aby odpowiedzieć na kilka punktów: combinezacznie działać, gdy tylko uruchomią się dwa somethingElsepolecenia, ale to jest OK, ponieważ <(…)rzeczy są potokami; więc combinebędzie po prostu zmuszony czekać na dane, jeśli przekroczy to somethingElseprocesy. Ponieważ są rurami, rozmiar nie stanowi problemu. … (Ciąg dalszy)
G-Man mówi „Przywróć Monikę”
(Ciąg dalszy)… Jedynym istotnym problemem, jaki mam z twoją odpowiedzią, jest to, że nie pozwala na sprawdzenie statusu wyjścia z somethingElseprocesów - i nie jest do końca jasne, czy jest to ważne dla pytającego. Ale odpowiedź nie powinna zadawać takich pytań.
G-Man mówi „Przywróć Monikę”
2

Możesz użyć waitpolecenia:

(echo starting & sleep 10 & wait) && echo done

Możesz zobaczyć, że linia „początkowa” dzieje się od razu, a „gotowe” czeka przez 10 sekund.

psusi
źródło
generalnie czekanie wymaga procesów potomnych tej samej powłoki. Czekanie jest dość trudne.
mikeserv
1
@mikeserv, o czym ty mówisz? O to chodzi: czeka na wszystkie dzieci w tej podpowłoce.
psusi
według moich pierwszych testów to działa. Spróbuję teraz na dużym skrypcie
Stephen Henderson
Dokładnie - dzieci w tym samym Shell - sub muszle. Powinien działać dla każdego procesu, który nie próbuje uciec - lub demonizować czy cokolwiek innego. To wszystko miałem na myśli - tak długo, jak twoje procesy szanują liderów procesów, czekanie jest w porządku, ale gdy tylko proces spróbuje zostać własnym liderem procesu, czekanie będzie miało problemy.
mikeserv
0

Dokładnie pokazuję, jak można tego dokonać w innej odpowiedzi tutaj . Ta odpowiedź była na pytanie o to, czy 2 dzienniki były utrzymywane przez proces w tle, więc zademonstrowałem to z 10.

Skrypt demonstracyjny

cat <<-\DEMO >|${s=/tmp/script} 
printf 'tty is %s\nparent pid is %s\npid is pid=%s\n' \
     "$(tty)" "$PPID" "$$"
exec 1>&2 ; nums=$(seq 0 9)
rm ${files=$(printf "/tmp/file%s\n" $nums)}
for n in $nums ; do { for f in $files ; do
    echo "Line $n" >>"$f" ; done
sleep 1 ; } ; done
#END
DEMO

Uruchom wersję demonstracyjną

s=/tmp/script ;chmod +x $s ;info="$(($s &)2>&- &)"
echo "$info" ; pid="${info##*=}" ; echo
while ps -p $pid >/dev/null ; do sleep 3 ; done
for f in /tmp/file[0-9] ; do
    printf 'path : %s\tline count : %s\n' \
        $f $(<$f wc -l)
done

Wynik:

tty is not a tty
parent pid is 1
pid is 12123

path : /tmp/file0    line count : 10
path : /tmp/file1    line count : 10
path : /tmp/file2    line count : 10
path : /tmp/file3    line count : 10
path : /tmp/file4    line count : 10
path : /tmp/file5    line count : 10
path : /tmp/file6    line count : 10
path : /tmp/file7    line count : 10
path : /tmp/file8    line count : 10
path : /tmp/file9    line count : 10

Powyższe pokazuje. Buduje i uruchamia skrypt o nazwie /tmp/script, chmod„s go jako plik wykonywalny, a uruchamia go w &backgroundtematyce &backgrounded ( subshell ).

Skrypt rms /tmp/file0-910 plików i echoeslinia co sekundę we wszystkich 10 z nich. Przechwytuję niektóre $infoz odrzuconego procesu i prezentuję je za pomocą $(command substitution). While psraportów z $pidprzechwytywania, wiem, że nadal działa, więc I sleep.Po zakończeniu wiersze we wszystkich 10 plikach są liczonewc.

Po wywołaniu procesu w ten sposób możesz swobodnie zamknąć jego pierwotny proces nadrzędny, a on będzie kontynuował przewóz - jest on skutecznie odrzucany. Oznacza to również, że nie można korzystać z tradycyjnego waitpolecenia, ale czeka na ps„s zwrotu powinny być bardziej wytrzymałe w każdym przypadku.

Warto wspomnieć, jak sądzę, jest to, że proces ten jest rzeczywiście początkowo wezwany $(command substitution)i printfsME $infoChcę więc mogę skutecznie kontrolować. Ale jak tylko upuści swoje wyjście terminalowe za pomocą exec 1>&2(który jest zamknięty w tej samej podpowłoce z 2>&-), proces ucieka i muszę czekać na to z drugiej strony. Niby najlepszy z obu światów, zwłaszcza jeśli używasz go do obsługi rur wejściowych, o ile możesz otoczyć wszystkie przekierowania i liderów procesów.

Wszystko inne służy tutaj tylko do demonstracji. Wszystko, czego potrzebujesz, aby uruchomić, to najwyższy skrypt i:

info="$(($script_path &)2>&- &)"    

UWAGA: To drukuje tylko na terminalu dokładnie to, co chciałem to zademonstrować. Jak zauważono w$PPID,tym procesie, ten proces jest odrzucany przez terminal i jest bezpośrednim potomkiem$PID 1.

Jeśli chcesz uruchomić dwa z nich jednocześnie i poczekać na nie, możesz po prostu pspodać oba ich numery i czekać.

mikeserv
źródło