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?

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

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ć?


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.


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ć.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.