LINUX.ORG.RU

Ожидание закрытия дочерних приложений - странности с выводом jobs

 , ,


0

1

Добрый день!

Пишу скрипт для цикличного бекапа БД и проверочного восстановления. Поскольку проверочное восстановление занимает достаточно много времени, то решил выполнять его в бекграунде и контролировать его окончание с помощью jobs. По ходу написания столкнулся со странной ситуацией: при вызове из скрипта утилиты jobs она выводит запущенные дочерние процессы и не выводит ничего, если таковых нет. Однако, при перенаправлении её вывода в grep или cat, она начинает выводить информацию об уже завершённых дочерних процессах. Например:

#!/bin/sh

while true ; do
  echo new while
  ( sleep 3 )&
  for i in {1..5} ; do
    jobs #| cat
    sleep 1
  done
done

В таком виде jobs выдаёт информацию только по текущему процессу, однако, если убрать решётку перед cat, то на выходе начнём получать информацию и о всех предыдущих дочерних процессах:

[1]   Done                    ( sleep 3 )
[2]   Done                    ( sleep 3 )
[3]   Done                    ( sleep 3 )
[4]+  Running                 ( sleep 3 ) &

Что такое стандартный вывод я в курсе, но я не понимаю, почему при выводе в консоль его содержимое отличается от того, что передаётся через перенаправление. Флаг -n для jobs на результат не влияет. Можете подсказать, где я туплю?

Поскольку проверочное восстановление занимает достаточно много времени, то решил выполнять его в бекграунде и контролировать его окончание с помощью jobs

Эээм...а зачем? Задача какая? Шелл какой, что ты решил решётку так ловко впендюрить?
Если совсем скучно то jobs -r, но лучше переделать по-человечески

zolden ★★★★★ ()

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

каждый 'jobs' в случае с перенаправлением запускается в новом дочернем процессе, который еще не получал данные о родственных процессах, поэтому ему вываливается вся история :)

по-моему, херню какую-то ваяешь;
лучше запоминай PID'ы нужных процессов и проверяй их потом так:
[ -d /proc/${PID} ] && echo still running

anonymous ()
Ответ на: комментарий от anonymous

2 zolden

Задача циклично бекапить БД (запускать один бекап сразу за другим), при этом периодически проверять резервные копии на возможность восстановления. Т.е. после каждого бекапа мы проверяем, запущен ли у нас сейчас рестор и если нет, то запускаем его фоном для копии, которую только что получили, и дальше отправляемся делать новую копию. Шелл - sh, а что не так с решёткой? Она же и bash'e и в sh'e комментарии отделяет от кода.

2 anonymous

А если после окончания рестора его пид займёт другой процесс? Может мне кажется, но не стоит полагаться на авось.

По поводу запуска в новом процессе - а это как-то можно предотвратить, или всегда при перенаправлении создаётся новый дочерний процесс?

Подумал, что можно просто парсить вывод jobs на наличие слова Running и перезапускать скрипт раз в сутки, чтобы история слишком большая не накапливалась. По идее - выход, хотя и не так красиво, как хотелось бы.

Schastliviy ()
Ответ на: комментарий от Schastliviy

А если после окончания рестора его пид займёт другой процесс? Может мне кажется, но не стоит полагаться на авось.

ну, я показал только направление поиска решения, а не конечный вариант; для более точного определения есть еще /proc/$PID/cmdline, /proc/$PID/status, а в нем PPid, pgrep в конце концов - изучай

По поводу запуска в новом процессе - а это как-то можно предотвратить, или всегда при перенаправлении создаётся новый дочерний процесс?

нельзя

Подумал, что можно просто парсить вывод jobs на наличие слова Running и перезапускать скрипт раз в сутки, чтобы история слишком большая не накапливалась. По идее - выход, хотя и не так красиво, как хотелось бы.

хозяин - барин!
я вообще не понял, зачем делать sleep в фоне, так что тебе виднее :)

anonymous ()
Ответ на: комментарий от Schastliviy

Задача циклично бекапить БД (запускать один бекап сразу за другим), при этом периодически проверять резервные копии на возможность восстановления. Т.е. после каждого бекапа мы проверяем, запущен ли у нас сейчас рестор и если нет, то запускаем его фоном для копии, которую только что получили, и дальше отправляемся делать новую копию.

не знаю, почему надо делать именно так, но я бы в этих условиях сделал следующее:
1) отдельный процесс бэкапа: тупо в цикле выполняет один бэкап, потом следующий и т.д.
2) отдельный процесс верификации бэкапов: тоже в цикле делает:
2.1) ищет самый свежий готовый бэкап;
2.2) если он уже проверялся (сохранять соответствующий флаг: файлик, БД), то засыпать на минуту, если - нет, то выполнять проверку;
тут возможны варианты, например, если свежайший бэкап уже проверен, то можно проверить самый свежий из непроверенных; или вообще развернуть процесс верификации наоборот, чтобы он всегда догонял процесс бэкапа, т.е. на каждом шаге искал самый старый из непроверенных; тут все зависит от иерархии важности бэкапов

anonymous ()
Ответ на: комментарий от anonymous

я вообще не понял, зачем делать sleep в фоне

Это просто для демонстрации того, о чём писал в начале. Вместо этого в реальном скрипте запускается процесс восстановления.

Решил проверять наличие фонового процесса так:

    jobs | grep Running > /dev/null
    if [ $? -eq 0 ]

В процессе написания пришёл к такому решению: Поскольку процесс восстановления в несколько раз медленнее бекапа, то проверяться будет не каждый бекап, а один из двух-трёх по следующему принципу: после создания бекапа мы проверяем, закончилось ли предыдущее задание рестора. Если не закончилось, то переносим созданный бекап в папку непроверенных, а если завершилось - то фоново запускаем его восстановление и по окончанию переносим в папку проверенных. Таким образом у нас бекапы делаются в режиме нон-стоп, но часть из них будет проверена на восстановление, а часть - нет, но зато создаваться они будут с минимальным интервалом. Если бекап/рестор не отработал - временный бекап удаляется, если отработался корректно - удаляем в соответствующей папке старую копию.

Schastliviy ()
Ответ на: комментарий от Schastliviy

Такой вопрос ещё возник: а если и родительский и дочерний процесс одновременно обратятся к файлу на запись (echo backup error >> $LOGFILE), конфликта не произойдёт? Или просто одна из операций записи завершится неудачно?

Schastliviy ()
Ответ на: комментарий от Schastliviy

а если и родительский и дочерний процесс одновременно обратятся к файлу на запись (echo backup error >> $LOGFILE), конфликта не произойдёт?

вообще говоря, конфликт может быть
хотя если куски, добавляемые к файлу, малы, то он (конфликт) маловероятен

anonymous ()

man bash


       wait [n ...]
              Wait for each specified process and return its termination status.  Each n may be a process ID or a job specification; if a job spec is given, all processes in that job's pipeline are waited for.  If n is  not  given,  all  currently
              active child processes are waited for, and the return status is zero.  If n specifies a non-existent process or job, the return status is 127.  Otherwise, the return status is the exit status of the last process or job waited for.

l5k ()
Ответ на: комментарий от l5k

Спасибо, про wait я знаю - не совсем то, что нужно - эта команда останавливает выполнение скрипта на время ожидания, а мне нужно только узнавать состояние и после этого продолжать скрипт

Schastliviy ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.