(bash) Сценарий A, подождите сценарий B, но не его дочерний процесс

9

Итак, у меня есть scriptA, который делает:

ssh server1 -- scriptB &
ssh server2 -- scriptB &
ssh server3 -- scriptB &
wait
otherstuffhappens

ScriptB делает:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
exit

Мой желаемый результат - scriptA будет ждать завершения всех экземпляров scriptB, прежде чем двигаться дальше, что он и делает в настоящее время, однако он также ожидает фоновых rsyncs не столь важных вещей. Это большие файлы, от которых я не хочу ждать.

Я прочитал разницу между nohup, disown и & и попробовал разные комбинации, но я не получил желаемого результата.

На данный момент я довольно озадачен. Любая помощь будет оценена!

Apoc
источник

Ответы:

15

Проблема здесь в том, что он sshdожидает окончания файла в канале, из которого он читает стандартный вывод команды (а не стандартный по какой-то причине, по крайней мере, с версией, на которой я тестирую). И фоновое задание наследует fd для этого канала.

Поэтому, чтобы обойти это, перенаправьте вывод этой фоновой rsyncкоманды в какой-нибудь файл или, /dev/nullесли вам это не важно. Вы также должны перенаправить stderr, потому что даже если sshd не ожидает соответствующего канала, после sshdвыхода канал будет разорван, поэтому rsyncон будет уничтожен, если попытается записать в stderr.

Так:

rsync ... > /dev/null 2>&1 &

Для сравнения:

$ time ssh localhost 'sleep 2 &'
ssh localhost 'sleep 2 &'  0.05s user 0.00s system 2% cpu 2.365 total
$ time ssh localhost 'sleep 2 > /dev/null &'
ssh localhost 'sleep 2 > /dev/null &'  0.04s user 0.00s system 12% cpu 0.349 total

А также:

$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null &'; sleep 2; cat out
141  # ls by killed with SIGPIPE upon writing the error message
$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null 2>&1 &'; sleep 2; cat out
2    # ls exited normally after writing the error on /dev/null instead
     # of a broken pipe
Стефан Шазелас
источник
4

Напишите скрипт, который является родителем обоих, тогда вы можете легко контролировать оба . В качестве альтернативы установите канал связи между двумя каналами .

Для того, чтобы конкретно игнорировать определенные фоновые задания вы можете захватить PID ( $!это последнее задание ПИД - справочного) и wait $pidждать только для этой работы , чтобы закончить.

l0b0
источник
3

-fФлаг sshисправляет проблему. Проверено на:

#!/bin/sh -e
echo $$_script__begin
( echo sleeping; sleep 2; echo slept )&
echo $$_script__end

Когда я запускаю его ssh localhost ./script, он ждет, пока не sleptпоявится. С -fфлагом он выходит в echo $$_script__endи sleptпозже появляется в фоновом режиме после того, как sshкоманда вернулась.

PSkocik
источник
2

Это известная проблема сервера OpenSSH, которая описана и обсуждается в вышестоящем bugzilla # 2071 . В сообщении об ошибке предлагается несколько обходных путей как на стороне OpenSSH, так и для сценария.

Если вы хотите ждать выхода из сценариев, вы должны добавить , waitпрежде чем exitиз scriptBтоже.

Если вас не волнует вывод, используйте некоторые варианты nohupперенаправления IO и к ним /dev/null, что решит проблему точно так же.

Jakuje
источник
1

Вы можете попробовать это. $!переменная оболочки по умолчанию, которая содержит идентификатор процесса последнего выполненного фонового конвейера / процесса.

command1 &
lpid1=$!

command2 &
lpid2=$!

command3 &
lpid=$!

wait $lpid1  # waits for only the process with PID lpid1  to complete. 

Вы должны использовать это в соответствии с вашим сценарием, используя exportпеременную и т. Д.

Каушик Наяк
источник
1

B просто нужно дождаться собственных фоновых процессов:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
wait
exit
Джаред Ловелл
источник
В этот момент вы можете просто не запускать второй rsync в фоновом режиме и избегать его использования waitполностью. Хотя я догадываюсь, что для OP требовалось запустить оба rsyncпроцесса параллельно, что означало бы задний фон их обоих&) и затем использование wait. В любом случае, я согласен с тем, что это самый простой способ решения проблемы, и я бы выбрал его, основываясь на информации в вопросе.
Дэвид З,