несколько фоновых процессов в сценарии

Это сложилось, что я просто забыл флаг начальной загрузки относительно раздела.

1
09.03.2013, 12:24
2 ответа

Если некоторое задание должно быть запущено после того, как некоторый файл был скопирован, просто сделайте это частью фонового задания:

(cp this /there && start job that needs this in /there) &
(cp that /here && start job that needs that in /here) &
wait

(последнее & не необходимо).

Теперь для более сложных зависимостей, Вы могли использовать GNU make -j.

make -j2 -f /dev/fd/3 3<< 'EOF'
all: j1 j2 j3
.PHONY: cp1 cp2 cp3 j1 j2 j3 all

cp1:
    cp this /there

cp2:
    cp that /here

cp3:
    cp this /here

j1: cp1
    start job that needs this in /there

j2: cp2
    start job that needs that in /here

j3: cp1 cp3
    start job that needs this in /here and /there
EOF

-j2 дошел бы до 2 заданий в любой момент времени, и зависимости будут уважать.

Теперь, чтобы не искажать файлов журнала у Вас есть две основных опции

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

Для 1, самое легкое должно сохранить каждый вывод задания в отдельном файле и объединить их впоследствии:

(cp this /there && start job that needs this in /there) > j1.log 2>&1 &
(cp that /here && start job that needs that in /here) > j2.log 2>&1 &
wait
cat j1.log j2.log > jobs.log

Другая опция состоит в том, чтобы использовать каналы, чтобы собрать вывод каждого задания и иметь cat объедините их. Замена процесса Shell как доступная в ksh, zsh или bash может помочь нам с этим и даже заботиться о фоновой обработке:

j1() { cp this /there && start job that needs this in /there; }
j2() { cp that /here && start job that needs that in /here; }
cat <(j1 2>&1) <(j2 2>&1) > jobs.log

j1, j2 и cat будет запущен одновременно и предаст земле соединенный с каналами.

Однако отметьте это cat только начнет читать из второго канала (который записан в j2) после j1 закончился. Это означает это если j2 записи больше входа, чем размер канала (например, на Linux, обычно 64 кибибита) j2 будет заблокирован до j1 концы.

При помощи этого можно избежать sponge от moreutils, как:

cat <(j1 2>&1) <(j2 2>&1 | sponge) > jobs.log

Хотя это означало бы весь вывод j2 был бы сохранен в памяти, и кошка только начнет писать вывод j2 в jobs.log после j2 закончился, в этом случае с помощью pv -qB 100M например, может быть предпочтительным:

cat <(j1 2>&1) <(j2 2>&1 | pv -qB 100M) > jobs.log

Тот путь j2 только приостановить (если j1 еще не закончился), после 100M (плюс два содержания канала) журналов были произведены, и pv не ожидал бы j2 закончиться прежде, чем произвести к stdout.

Обратите внимание, что для всего вышеупомянутого, необходимо остерегаться этого, после того как Вы перенаправляете вывод большинства команд в файл или канал (что-либо кроме tty), поведение затронуто. Команды, или скорее stdio API libc они звонят (printf, fputs, fwrite...) обнаруживает, что вывод не идет в терминал, и выполните оптимизацию путем вывода в больших блоках (несколько килобайтов), в то время как они не делают этого для стандартной погрешности. Это означает порядок выходных сигналов, и сообщения об ошибках будут затронуты. Если это - проблема в системах GNU или FreeBSD (по крайней мере), и для динамично связанных команд, можно использовать stdbuf команда:

stdbuf -oL j1 > j1.log 2>&1

вместо:

j1 > j1.log 2>&1

гарантировать, что вывод stdio с буфером строки (каждая строка вывода будет записана отдельно, как только они завершены).

Для опции 2, пишет в канал меньше, чем PIPE_BUF байты, который на Linux составляет 4 096 байтов, настолько больше, чем Ваша средняя строка журнала, как гарантируют, будут атомарными, это - то, если два процесса запишут в тот же канал одновременно, то их 2 записи, как гарантируют, не будут переплетены. Нет такой гарантии на регулярных файлах, но я серьезно сомневаюсь, что 2 записи меньше чем 4 кибибитов могли закончиться, переплелся на любой ОС или файловой системе.

Таким образом если бы не та буферизация, описанная выше и если бы строки журнала были произведены индивидуально в целом и отдельно, у Вас была бы гарантия, что строки вывода не будут иметь части строки этого задания и части строки того другого задания.

Однако ничто не предотвращает команду, чтобы сделать сброс промежуточные две части записанной строки (как printf("foo"); fflush(stdout); printf("bar\n");) и на stderr нет никакой буферизации.

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

Можно решить обе проблемы путем выполнения чего-то как:

tag() { stdbuf -oL sed "s%^%$1: %"; }
{
  j1 2>&1 | tag j1 &
  j2 2>&1 | tag j2
} | cat > jobs.log

(обратите внимание, что нам не нужно wait (и это не работало бы так или иначе в большинстве оболочек), потому что cat не закончится, пока не будет никого пишущего в канал больше, таким образом, только в j1 и j2 завершились).

Выше мы используем | cat иметь канал с его гарантией атомарности. Мы передаем вывод по каналу каждой команды к команде, которая отмечает каждую строку с именем задания. j1 и j2 может записать их вывод однако, они хотят, sed (из-за stdbuf -oL) произведет строки (с префиксом тега) в целом и отдельно, который гарантирует вывод, который не будет искажен.

То же примечание как выше все еще применяется: мы не подаем заявку stdbuf -oL к командам в j1 и j2 таким образом, они, скорее всего, буферизуют свой вывод, который может поэтому быть записан еще долго после того, как он был произведен. Это еще хуже, чем в предыдущем случае, потому что, если Вы видите:

j1: doing A
j1: doing B
j2: doing C

Это действительно означает это j1 сделал прежде, чем сделать B, но не, что он сделал любого из них прежде j2 выполнение C. Так еще раз Вы, возможно, должны подать заявку stdbuf -oL к большему количеству команд, если это - проблема.

Обратите внимание, что Вы не можете подать заявку stdbuf окружать функции как j1 или j2 выше, но по крайней мере с GNU и FreeBSD stdbuf, можно использовать это для установки stdbuf глобально или на основе на подоболочку:

stdbuf_LD_PRELOAD=$(stdbuf sh -c 'export -p LD_PRELOAD')
line_buffered_output() {
  eval "$stdbuf_LD_PRELOAD"
  export _STDBUF_O=L
}
j1() (line_buffered_output; cp this /there && start...)
3
27.01.2020, 23:17

Если каждая загруженная обработка потребности файла, сделайте:

cp whatever file; process file &

вместо этого.

Если Вы волнуетесь, что файлы журнала искажены, возможно, необходимо использовать syslog(3), возможно, через logger(1). Рассмотреть использование nohup(1) если кто-то мог бы выйти из системы (и уничтожить фоновые процессы выполнения непреднамеренно).

2
27.01.2020, 23:17

Теги

Похожие вопросы