Bash, як дозволити деяким фоновим процесам працювати, а чекати інших?


11

У мене є (ще) інший wait, &, &&питання потоку управління ..

Скажіть, у мене є такий сценарій, коли я хочу зробити якомога більше роботи одночасно:

# 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

Питання 1: Чи в сценарії combineчекають, коли обидва somethingElseпроцеси закінчаться, поки обидва somethingпроцеси триватимуть?

Питання 2: Якщо ні - і я підозрюю, що це не так - як я змушусь combineчекати лише обох somethingElseпроцесів, в той час як somethingвищезгадані процеси продовжують працювати у фоновому режимі?

Відповіді:


13

У вашому прикладі combineкоманда буде просто запущена, як тільки підпункт завершиться (і за умови запуску останнього фонового процесу без помилки). Підзаголовок вийде відразу після запуску завдань, оскільки немає waitкоманди.

Якщо ви хочете виконати команду на основі значення повернення двох або більше одночасних фонових процесів, я не можу побачити іншого способу, крім використання тимчасових файлів для повернених значень. Це тому, що waitможе повернути лише повернене значення одного з процесів, який він чекає. Оскільки фонові процеси повинні бути запущені в підрозділах, щоб взагалі отримати їх повернені значення, вони не можуть зберігатися у змінних. Ви можете зробити:

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"

Якщо вам не дуже важливо повертаються значення, ви можете просто почати завдання і використовувати wait:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result

Привіт, я думаю, що другий варіант спрацював би для мене ...
Стівен Хендерсон

3

Чи буде заміна процесів більш ефективною, особливо якщо вам не потрібно зберігати файли OutputAі OutputB, і дбаєте лише про них Result? Це особливо економить час, оскільки якщо у вас є повільний запис вводу-виводу на диск, збереження файлів OutputAі, OutputBможливо, це крок, що обмежує швидкість?

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

Підстановка процесу дозволяє вставити команду всередину, <(..here..)а не зберегти вихід у файл, а потім прочитати з нього як вхід на етапі "об'єднати".

Якщо пам'ять є обмеженням, а розмір outputAі outputBбільше, ніж пам'ять може утримувати, чи переможе це цілі?

Зачекає, combineпоки обидва процеси завершаться, перш ніж він почне працювати


Це не «небезпека»; будь ласка, не формулюйте свою відповідь у формі запитання. Серйозно, ви придумали нову ідею, і я думаю, що це досить добре. Відповідь на пару ваших пунктів: combineпочне працювати, як тільки дві somethingElseкоманди запустяться, але це нормально, тому що <(…)речі - це труби; тому combineпросто змушені будуть чекати даних, якщо це випереджає somethingElseпроцеси. А тому, що це труби, розмір - це не проблема. … (Продовження)
G-Man каже: «Відновити Моніку»

(Продовжував)… Єдина суттєва проблема, що виникає у вашій відповіді, полягає в тому, що вона не дозволяє перевірити статус виходу з somethingElseпроцесів - і не зовсім зрозуміло, чи важливо це для запитувача. Але також відповідь не повинна ставити такі запитання.
G-Man каже: "Відновіть Моніку"

2

Ви можете використовувати waitкоманду:

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

Ви можете бачити, що "стартовий" рядок відбувається одразу, а "готовий" чекає 10 секунд.


як правило, чекати потрібні дочірні процеси однієї і тієї ж оболонки. Зачекайте там досить хитро.
mikeserv

1
@mikeserv, про що ти говориш? Це суть: він чекає всіх дітей, які перебувають у цій зоні.
psusi

за моїми початковими тестами це працює. Зараз я спробую спробувати його на великому сценарії
Стівен Хендерсон

Точно - діти однієї і тієї ж оболонки - під снаряди. Він повинен працювати для будь-якого процесу, який не намагається втекти - або демонізувати чи що завгодно. Це все, що я мав на увазі - доки ваші процеси поважають лідерів процесів, чекайте нормально, але як тільки процес спробує стати його власним керівником процесу, чекати виникнуть проблеми.
mikeserv

0

Я на самому ділі продемонструвати , як саме такого роду речі можна було б зробити в інший відповідь тут . Ця відповідь була на запитання про те, щоб два журнали підтримувалися фоновим процесом, тому я продемонстрував це за допомогою 10.

Демо сценарій

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

Запустити демонстрацію

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

Вихід:

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

Сказане вище демонструє. Він будує і запускає скрипт з ім'ям /tmp/script, chmod«S як виконуваний файл і запускає його в &backgroundз &backgrounded ( subshell ).

Сценарій rms /tmp/file0-910 файлів і echoesрядки щосекунди на всі 10 з них. Я захоплюю частину $infoз відключеного процесу і представляю його через $(command substitution). While psнерухомі звіти про $pidзахоплення, я знаю, що він все ще працює, тому я sleep.Коли він завершується, рядки у всіх 10 файлах підраховуютьсяwc.

Після того, як ви викликаєте процес таким чином, ви можете вільно закрити його початковий батьківський процес, і він продовжуватиме перевірку - він фактично відмовляється. Це також означає , що ви не можете використовувати звичайну waitкоманду, але очікування ps«S повернення повинні бути більш надійними в будь-якому випадку.

Варто відзначити, я думаю, що цей процес на самому справі спочатку викликав $(command substitution)і printfsмені $infoя хочу , щоб я міг ефективно контролювати. Але як тільки він скидає свій термінальний вихід з exec 1>&2(який закритий у тій же підпалі 2>&-), процес виходить з ладу, і мені доведеться чекати його на іншому кінці. Начебто найкраще з обох світів, особливо якщо ви використовуєте його для обробки вхідних труб, якщо ви зможете обернутись усіма перенаправленнями та лідерами процесів.

Все інше тут лише для демонстрації. Все, що вам потрібно для запуску, - це верхній сценарій і:

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

ПРИМІТКА. Це тільки друкує на термінал саме те, що я хотів продемонструвати. Як зазначається в$PPID,цьому процесі, цей процес відхиляється від терміналу і є безпосередньою дитиною$PID 1.

Якщо ви хотіли одночасно запустити два з них і чекати їх, ви можете просто вручити psобидва їхні припуски і зачекати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.