Почему я не могу использовать управление заданиями в bash script?
В этот ответ на другой question мне сказали, что
в скриптах, у вас нет управления заданиями (и попытка включить его глупо)
Это первый раз, когда я это услышал, и я прочитал раздел bash.info в разделе "Управление заданиями" (глава 7), не нахожу никакого упоминания ни об одном из этих утверждений. [ Обновление:. Страница руководства немного лучше, упоминание об "типичном" использовании, настройках по умолчанию и терминальных вводах-выводах, но не имеет реальной причины, почему контроль работы особенно не рекомендуется для скриптов.]
Итак, почему не работает работа по управлению заданиями на основе script, и что делает ее плохой практикой (иначе называемой "глупый" )?
Изменить: script запускает фоновый процесс, запускает второй фоновый процесс, затем пытается перенести первый процесс на передний план так, чтобы он имел обычный терминал ввода/вывода ( как если бы они выполнялись напрямую), которые затем могут быть перенаправлены извне script. Не удается сделать это в фоновом режиме.
Как отмечено принятым ответом на другой вопрос, существуют другие сценарии, которые решают эту проблему, не пытаясь контролировать работу. Хорошо. И lambasted script использует жесткий номер задания — Очевидно, плохо. Но я пытаюсь понять, является ли контроль над работой принципиально обреченным подходом. По-прежнему кажется, что это может сработать...
Ответы
Ответ 1
Что он имел в виду, так это то, что управление заданиями по умолчанию отключено в неинтерактивном режиме (т.е. в script.)
На странице bash
man:
JOB CONTROL
Job control refers to the ability to selectively stop (suspend)
the execution of processes and continue (resume) their execution at a
later point.
A user typically employs this facility via an interactive interface
supplied jointly by the system’s terminal driver and bash.
и
set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
...
-m Monitor mode. Job control is enabled. This option is on by
default for interactive shells on systems that support it (see
JOB CONTROL above). Background processes run in a separate
process group and a line containing their exit status is
printed upon their completion.
Когда он сказал "глупо", он имел в виду, что не только:
- - это управление заданиями, предназначенное в основном для облегчения интерактивного управления (тогда как script может работать непосредственно с pid's), но также
- Я цитирую его оригинальный ответ,... полагается на то, что вы ранее не запускали какие-либо другие задания в script, что является плохим предположением. Что совершенно правильно.
UPDATE
В ответ на ваш комментарий: да, никто не остановит вас от использования управления заданиями в bash script - нет жесткого случая принудительного отключения set -m
(т.е. да, управление заданиями из script будет работать, если вы этого захотите.) Помните, что в конце концов, особенно в сценариях, всегда есть несколько способов кошки кошки, но некоторые способы более переносимы, более надежны, упрощают обработку ошибок, проанализировать вывод и т.д.
Особые обстоятельства могут или не могут отличаться от того, что lhunath
(и другие пользователи) считают "лучшими практиками".
Ответ 2
Управление заданиями с помощью bg
и fg
полезно только в интерактивных оболочках. Но &
в сочетании с wait
также полезен в скриптах.
В многопроцессорных системах нерестовые задания фона могут значительно улучшить производительность script, например. в сценариях сборки, где вы хотите запустить хотя бы один компилятор на один процессор, или обрабатывать изображения с помощью инструментов ImageMagick параллельно и т.д.
В следующем примере выполняется до 8 параллельных gcc для компиляции всех исходных файлов в массиве:
#!bash
...
for ((i = 0, end=${#sourcefiles[@]}; i < end;)); do
for ((cpu_num = 0; cpu_num < 8; cpu_num++, i++)); do
if ((i < end)); then gcc ${sourcefiles[$i]} & fi
done
wait
done
В этом нет ничего глупого. Но вам понадобится команда wait
, которая ждет всех фоновых заданий до script. PID последнего задания фона хранится в переменной $!
, поэтому вы также можете wait ${!}
. Также обратите внимание на команду nice
.
Иногда такой код полезен в make файлах:
buildall:
for cpp_file in *.cpp; do gcc -c $$cpp_file & done; wait
Это дает намного более тонкое управление, чем make -j
.
Обратите внимание, что &
является терминатором строки, подобным ;
(напишите command&
not command&;
).
Надеюсь, что это поможет.
Ответ 3
Управление заданиями полезно только тогда, когда вы запускаете интерактивную оболочку, то есть знаете, что stdin и stdout подключены к терминальному устройству (/dev/pts/* в Linux). Тогда имеет смысл иметь что-то на переднем плане, что-то еще на заднем плане и т.д.
Сценарии, с другой стороны, не имеют такой гарантии. Скрипты могут быть выполнены исполняемыми и запускаться без подключения терминала. В этом случае нет смысла иметь передние или фоновые процессы.
Однако вы можете запускать другие команды неинтерактивно на фоне (добавляя "&" в командную строку) и записывать их PID с помощью $!
. Затем вы используете kill
, чтобы убить или приостановить их (имитируя Ctrl-C или Ctrl-Z на терминале, это оболочка была интерактивной). Вы также можете использовать wait
(вместо fg
), чтобы дождаться завершения фонового процесса.
Ответ 4
Может быть полезно включить управление заданиями в script, чтобы установить ловушки на
SIGCHLD. Раздел JOB CONTROL в руководстве говорит:
Оболочка сразу узнает, когда изменяется состояние задания. Как обычно, bash ожидает, пока он собирается распечатать приглашение перед отчетностью изменения в статусе задания, чтобы не прерывать какой-либо другой выход. Если опция -b для установленной встроенной команды включена, bash отчетов такие изменения немедленно. Любая ловушка в SIGCHLD выполняется для каждого дочерний элемент, который завершает работу.
(акцент мой)
В качестве примера возьмем script:
[email protected]:~$ cat children.bash
#!/bin/bash
set -m
count=0 limit=3
trap 'counter && { job & }' CHLD
job() {
local amount=$((RANDOM % 8))
echo "sleeping $amount seconds"
sleep "$amount"
}
counter() {
((count++ < limit))
}
counter && { job & }
wait
[email protected]:~$ chmod +x children.bash
[email protected]:~$ ./children.bash
sleeping 6 seconds
sleeping 0 seconds
sleeping 7 seconds
Примечание: Улавливание CHLD, по-видимому, нарушено как bash 4.3
В bash 4.3 вы можете использовать "wait -n" для достижения того же,
хотя:
[email protected]:~$ cat waitn.bash
#!/home/dualbus/local/bin/bash
count=0 limit=3
trap 'kill "$pid"; exit' INT
job() {
local amount=$((RANDOM % 8))
echo "sleeping $amount seconds"
sleep "$amount"
}
for ((i=0; i<limit; i++)); do
((i>0)) && wait -n; job & pid=$!
done
[email protected]:~$ chmod +x waitn.bash
[email protected]:~$ ./waitn.bash
sleeping 3 seconds
sleeping 0 seconds
sleeping 5 seconds
Вы можете утверждать, что есть другие способы сделать это в более
переносимый путь, то есть без CHLD или wait -n:
[email protected]:~$ cat portable.sh
#!/bin/sh
count=0 limit=3
trap 'counter && { brand; job & }; wait' USR1
unset RANDOM; rseed=123459876$$
brand() {
[ "$rseed" -eq 0 ] && rseed=123459876
h=$((rseed / 127773))
l=$((rseed % 127773))
rseed=$((16807 * l - 2836 * h))
RANDOM=$((rseed & 32767))
}
job() {
amount=$((RANDOM % 8))
echo "sleeping $amount seconds"
sleep "$amount"
kill -USR1 "$$"
}
counter() {
[ "$count" -lt "$limit" ]; ret=$?
count=$((count+1))
return "$ret"
}
counter && { brand; job & }
wait
[email protected]:~$ chmod +x portable.sh
[email protected]:~$ ./portable.sh
sleeping 2 seconds
sleeping 5 seconds
sleeping 6 seconds
Итак, в заключение, set -m не, что полезно в скриптах, поскольку
единственная интересная особенность, которую она привносит в сценарии, - это возможность
работать с SIGCHLD. И есть другие способы добиться того же самого
либо более короткие (ждать -n) или более переносимые (отправляя сигналы сами).
Ответ 5
Bash поддерживает управление заданиями, как вы говорите. При написании оболочки script часто возникает предположение, что вы не можете полагаться на то, что у вас есть bash, но у вас есть ванильная оболочка Bourne (sh
), которая исторически не имела управления заданиями.
Мне сложно в эти дни представить себе систему, в которой вы честно ограничены реальной оболочкой Bourne. Большинство систем /bin/sh
будут связаны с bash
. Тем не менее, это возможно. Единственное, что вы можете сделать, это не указывать
#!/bin/sh
Вы можете сделать:
#!/bin/bash
Это и ваша документация помогут вам очистить ваши script потребности bash
.
Ответ 6
Возможно, o/t, но я довольно часто использую nohup, когда ssh на сервере работает на длительной работе, так что, если я выйду из системы, работа все равно будет завершена.
Интересно, запутались ли люди и начали ли они с мастер-интерактивной оболочки и нереста фоновых процессов? Команда wait позволяет вам генерировать множество вещей, а затем ждать их завершения и, как я уже сказал, я все время использую nohup. Это сложнее, чем это и очень малоиспользуемый - sh поддерживает этот режим тоже. Посмотрите руководство.
У вас также есть
kill -STOP pid
Я довольно часто это делаю, если хочу приостановить текущую работу sudo, как в:
kill -STOP $$
Но горе вам, если вы выскочили в оболочку из редактора - все будет сидеть там.
Я имею тенденцию использовать мнемонический -KILL и т.д., потому что существует опасность ввода
kill - 9 pid # note the space
и в прежние времена вы иногда могли вывести машину из-за того, что она убила init!
Ответ 7
задания DO работают в bash скриптах
НО, вам... НУЖНО следить за созданным персоналом
как:
ls -1 /usr/share/doc/ | while read -r doc ; do ... done
задания будут иметь разный контекст с каждой стороны |
обход этого может использоваться вместо:
for `ls -1 /usr/share/doc` ; do ... done
это должно продемонстрировать, как использовать задания в script...
с упоминанием о том, что мое примечание примечание... REAL (не знаю, почему это поведение)
#!/bin/bash
for i in `seq 7` ; do ( sleep 100 ) & done
jobs
while [ `jobs | wc -l` -ne 0 ] ; do
for jobnr in `jobs | awk '{print $1}' | cut -d\[ -f2- |cut -d\] -f1` ; do
kill %$jobnr
done
#this is REALLY ODD ... but while won't exit without this ... dunno why
jobs >/dev/null 2>/dev/null
done
sleep 1
jobs