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
Graeme
источник
Привет, я думаю, что второй вариант будет работать для меня ...
Стивен Хендерсон
3

Замена Would процесс более эффективным, особенно , если вам не нужно сохранять файлы OutputAи OutputB, и заботиться только о том Result? Будет ли это особенно экономить время, потому что если у вас медленный ввод-вывод при записи на диск, сохранение файлов OutputAи OutputBможет быть ограничивающим скорость шагом?

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

Подстановка процессов позволяет поместить команду внутрь, <(..here..)а не сохранять выходные данные в файл, а затем читать их как входные данные на этапе «объединить».

Если память является ограничением, а объем outputAи даже outputBбольше, чем может вместить память, победит ли она всю цель?

Будет ли combineждать завершения обоих процессов, прежде чем он запустится?

TW Tan
источник
Это не «Опасность»; пожалуйста, не формулируйте свой ответ в форме вопроса. Серьезно, у вас появилась новая идея, и я думаю, что она довольно хорошая. Чтобы ответить на пару ваших моментов: combineначнется выполнение, как только начнутся две somethingElseкоманды, но это нормально, потому что <(…)все это каналы; поэтому combineпросто будет вынужден ждать данных, если они опережают somethingElseпроцессы. И, поскольку они представляют собой трубы, размер не является проблемой. … (Продолжение)
G-Man говорит «Восстановить Монику»
(Продолжение)… Единственная существенная проблема, с которой я столкнулся с вашим ответом, заключается в том, что он не позволяет проверить состояние выхода somethingElseпроцессов - и не совсем ясно, важно ли это для автора. Но, кроме того, ответ не должен задавать такие вопросы.
G-Man говорит: «Восстанови Монику»
2

Вы можете использовать waitкоманду:

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

Вы можете видеть, что «стартовая» строка происходит сразу, а «готово» ждет 10 секунд.

psusi
источник
обычно для ожидания требуются дочерние процессы одной оболочки. Подождите, там довольно сложно.
mikeserv
1
@mikeserv, о чем ты говоришь? В том-то и дело: он ждет всех детей в этой оболочке.
psusi
по моим первоначальным тестам это работает. Я собираюсь попробовать это на большом сценарии сейчас
Стивен Хендерсон
Точно - дети одной и той же оболочки - подчиненные оболочки. Он должен работать для любого процесса, который не пытается сбежать - или демонизировать или что-то еще. Это все, что я имел в виду - пока ваши процессы уважают лидеров процессов, все в порядке, но как только процесс попытается стать его собственным лидером, у ждать будут проблемы.
mikeserv
0

Я на самом деле продемонстрировать , как именно такого рода вещи можно было бы сделать в другой ответ здесь . Этот ответ был на вопрос о том, что 2 журнала были сохранены в фоновом режиме, поэтому я продемонстрировал это с 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захвате I, я знаю, что они все еще выполняются, поэтому я. sleep.После завершения строки во всех 10 файлах подсчитываются сwc.

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

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

Все остальное здесь просто для демонстрации. Все, что вам нужно для запуска, это верхний скрипт и:

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

ПРИМЕЧАНИЕ. Это выводит на терминал только то, что я хотел продемонстрировать. Как отмечено,$PPID,этот процесс отвергается терминалом и является прямым потомком$PID 1.

Если вы хотите запустить два из них одновременно и ждать их, вы можете просто сдать psоба пида и подождать.

mikeserv
источник