Использование GNU screen
Ваш лучший выбор.
Выполнение экрана "Пуск", когда Вы сначала входите в систему - я работаю screen -D -R
, выполните свою команду, и или разъедините или приостановите ее с CTRL-Z
и затем разъединение с экрана путем нажатия CTRL-A
затем D
.
Когда Вы входите в машину снова, снова соединяетесь путем выполнения screen -D -R
. Вы будете в той же оболочке как прежде. Можно работать jobs
видеть приостановленный процесс, если Вы сделали так и работали %1
(или соответствующее задание #) к переднему плану это снова.
Вот другой способ сделать привязывающийся сценарий оболочки, который может предотвратить состояние состязания, которое Вы описываете выше, куда два задания могут оба передать строку 3. noclobber
опция будет работать в ksh и ударе. Не использовать set noclobber
потому что Вы не должны писать сценарий в csh/tcsh.;)
lockfile=/var/tmp/mylock
if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then
trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
# do stuff here
# clean up after yourself, and release your trap
rm -f "$lockfile"
trap - INT TERM EXIT
else
echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi
YMMV с соединением NFS (Вы знаете, когда серверы NFS не достижимы), но в целом это намного более устойчиво, чем это раньше было. (10 лет назад)
Если у Вас есть задания крона, которые делают то же самое одновременно с нескольких серверов, но Вам только нужен 1 экземпляр для фактического выполнения, что-то вроде этого могло бы работать на Вас.
У меня нет опыта с lockrun, но наличие предварительно установленной среды блокировки до сценария, на самом деле работающего, могло бы помочь. Или это не могло бы. Вы просто устанавливаете тест для lockfile вне Вашего сценария в обертке, и теоретически, Вы не могли только поразить то же состояние состязания, если бы два задания назвал lockrun в точно то же время, так же, как с решением 'внутреннего сценария'?
Захват файла является в значительной степени поведением системы чести так или иначе, и любые сценарии, которые не проверяют на существование lockfile до выполнения, сделают то, что они собираются сделать. Только путем включения теста lockfile и правильного поведения, Вы будете решать 99% потенциальных проблем, если не 100%.
При столкновении с lockfile условиями состязания много это может быть индикатор большей проблемы, как не синхронизация заданий право, или возможно если интервал не так важен как завершение задания, возможно, задание лучше подходит быть daemonized.
Основа на комментарии @Clint Pachl ниже при использовании ksh88 использует mkdir
вместо noclobber
. Это главным образом смягчает потенциальное состояние состязания, но не полностью ограничивает его (хотя риск миниатюрен). Для получения дополнительной информации читайте ссылку, которую Clint отправил ниже.
lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid
if ( mkdir ${lockdir} ) 2> /dev/null; then
echo $$ > $pidfile
trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
# do stuff here
# clean up after yourself, and release your trap
rm -rf "$lockdir"
trap - INT TERM EXIT
else
echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi
И, как добавленное преимущество, если необходимо создать tmpfiles в сценарии, можно использовать lockdir
каталог для них, зная они будут очищены, когда сценарий выйдет.
Для более современного удара noclobber метод наверху должен подойти.
Не используйте файл.
Если Ваш сценарий выполняется как это, например:
bash my_script
Можно обнаружить, если это выполняет использование:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
my_script
? В случае другой экземпляр работает - не делает running_proc
содержать два согласующих отрезка длинной линии? Мне нравится идея, но конечно - Вы получите ложные результаты, когда другой пользователь запустит скрипт с тем же именем... спасибо
– maxschlepzig
05.10.2011, 11:20
$!
вместо $$
в Вашем примере.
– maxschlepzig
05.10.2011, 15:02
grep -v $$
). В основном я пытался обеспечить другой подход к проблеме.
– frogstarr78
05.10.2011, 20:39
Я понимаю это mkdir
является атомарным, так возможно:
lockdir=/var/tmp/myapp
if mkdir $lockdir; then
# this is a new instance, store the pid
echo $$ > $lockdir/PID
else
echo Job is already running, pid $(<$lockdir/PID) >&2
exit 6
fi
# then set traps to cleanup upon script termination
# ref http://www.shelldorado.com/goodcoding/tempfiles.html
trap 'rm -r "$lockdir" >/dev/null 2>&1' 0
trap "exit 2" 1 2 3 13 15
mkdir()
по NFS (> =3) стандартизирован, чтобы быть атомарным.
– maxschlepzig
05.10.2011, 00:05
mkdir
быть атомарным (это делает для rename
). На практике известно, что некоторые реализации не.Похожие страницы: интересный поток, включая вклад автором дуги GNU.
– Gilles 'SO- stop being evil'
05.10.2011, 01:12
Я предпочитаю использовать жесткие ссылки.
lockfile=/var/lock/mylock
tmpfile=${lockfile}.$$
echo $$ > $tmpfile
if ln $tmpfile $lockfile 2>&-; then
echo locked
else
echo locked by $(<$lockfile)
rm $tmpfile
exit
fi
trap "rm ${tmpfile} ${lockfile}" 0 1 2 3 15
# do what you need to
Жесткие ссылки являются атомарными по NFS и по большей части, mkdir также. Используя mkdir(2)
или link(2)
о том же, на практическом уровне; я просто предпочитаю использовать жесткие ссылки, потому что больше реализаций NFS позволило атомарные жесткие ссылки, чем атомарный mkdir
. С современными выпусками NFS Вам не придется взволновать использование также.
Я использую инструмент командной строки "скопление" для управления, привязывает мои сценарии удара, как описано здесь и здесь. Я использовал этот простой метод из страницы справочника скопления, для выполнения некоторых команд в подоболочке...
(
flock -n 9
# ... commands executed under lock ...
) 9>/var/lock/mylockfile
В том примере это перестало работать с кодом выхода 1, если это не может получить lockfile. Но скопление может также использоваться способами, которые не требуют, чтобы команды были выполнены в подоболочке :-)
flock()
и таким образом проблематично по NFS. Btw, тем временем, скопление () на Linux теперь отступают к fcntl()
когда файл расположен на NFS, монтируются, таким образом, в среде NFS только для Linux flock()
теперь работает по NFS.
– maxschlepzig
08.05.2016, 00:24
Я предлагаю следующее решение в сценарии, названном 'flocktest'
#!/bin/bash
export LOGFILE=`basename $0`.logfile
logit () {
echo "$1" >>$LOGFILE
}
PROGPATH=$0
(
flock -x -n 257
(($?)) && logit "'$PROGPATH' is already running!" && exit 0
logit "'$PROGPATH', proc($$): sleeping 30 seconds"
sleep 30
)257<$PROGPATH
Проверьте мой сценарий...
Можно ЛЮБИТЬ его....
[rambabu@Server01 ~]$ sh Prevent_cron-OR-Script_against_parallel_run.sh
Parallel RUN Enabled
Now running
Task completed in Parallel RUN...
[rambabu@Server01 ~]$ cat Prevent_cron-OR-Script_against_parallel_run.sh
#!/bin/bash
#Created by RambabuKella
#Date : 12-12-2013
#LOCK file name
Parallel_RUN="yes"
#Parallel_RUN="no"
PS_GREP=0
LOCK=/var/tmp/mylock_`whoami`_"$0"
#Checking for the process
PS_GREP=`ps -ef |grep "sh $0" |grep -v grep|wc -l`
if [ "$Parallel_RUN" == "no" ] ;then
echo "Parallel RUN Disabled"
if [ -f $LOCK ] || [ $PS_GREP -gt 2 ] ;then
echo -e "\nJob is already running OR LOCK file exists. "
echo -e "\nDetail are : "
ps -ef |grep "$0" |grep -v grep
cat "$LOCK"
exit 6
fi
echo -e "LOCK file \" $LOCK \" created on : `date +%F-%H-%M` ." &> $LOCK
# do some work
echo "Now running"
echo "Task completed on with single RUN ..."
#done
rm -v $LOCK 2>/dev/null
exit 0
else
echo "Parallel RUN Enabled"
# do some work
echo "Now running"
echo "Task completed in Parallel RUN..."
#done
exit 0
fi
echo "some thing wrong"
exit 2
[rambabu@Server01 ~]$
lockfile
, обычно поставляемый с пакетом [1127359] procmail[1127360].
FLOM позволяет реализовать более софистичные варианты использования (распределенная блокировка, читатели/писатели, числовые ресурсы и т.д.......................... как описано здесь: [112554]http://sourceforge.net/p/flom/wiki/FLOM%20by%20examples/
Вот кое-что, что я иногда добавляю на сервер, чтобы легко обрабатывать условия гонки для любых заданий на машине. Это похоже на сообщение Тима Кеннеди, но таким образом вы получаете обработку гонки, добавляя только одну строку в каждый сценарий bash, который в ней нуждается.
Поместите содержимое ниже, например, в / opt / racechecker / racechecker:
ZPROGRAMNAME=$(readlink -f $0)
EZPROGRAMNAME=`echo $ZPROGRAMNAME | sed 's/\//_/g'`
EZMAIL="/usr/bin/mail"
EZCAT="/bin/cat"
if [ -n "$EZPROGRAMNAME" ] ;then
EZPIDFILE=/tmp/$EZPROGRAMNAME.pid
if [ -e "$EZPIDFILE" ] ;then
EZPID=$($EZCAT $EZPIDFILE)
echo "" | $EZMAIL -s "$ZPROGRAMNAME already running with pid $EZPID" alarms@someemail.com >>/dev/null
exit -1
fi
echo $$ >> $EZPIDFILE
function finish {
rm $EZPIDFILE
}
trap finish EXIT
fi
Вот как его использовать. Обратите внимание на строку после shebang:
#/bin/bash
. /opt/racechecker/racechecker
echo "script are running"
sleep 120
Он работает так, что он определяет имя основного файла bashscript и создает файл pid в каталоге "/ tmp". Он также добавляет слушателя к сигналу финиша. Слушатель удалит файл pid, когда основной скрипт завершится должным образом.
Вместо этого, если при запуске экземпляра существует pid-файл, то будет выполнен оператор if, содержащий код внутри второго оператора if. В этом случае я решил отправить сообщение о тревоге, когда это произойдет.
Что делать, если сценарий дает сбой
Следующим упражнением будет обработка сбоев. В идеале файл pidfile должен быть удален, даже если основной скрипт вылетает по какой-либо причине, в моей версии выше этого не делается. Это означает, что в случае сбоя сценария файл pid должен быть удален вручную для восстановления работоспособности.
В случае сбоя системы
Рекомендуется сохранить файл pidfile / lockfile, например, в каталоге / tmp. Таким образом, ваши сценарии обязательно продолжат выполнение после сбоя системы, поскольку файлы pid всегда будут удаляться при загрузке.
sem
, который входит в состав инструментов GNU parallel
, может быть тем, что вы ищете:
sem [--fg] [--id <id>] [--semaphoretimeout <secs>] [-j <num>] [--wait] command
Как в:
sem --id my_semaphore --fg "echo 1 ; date ; sleep 3" &
sem --id my_semaphore --fg "echo 2 ; date ; sleep 3" &
sem --id my_semaphore --fg "echo 3 ; date ; sleep 3" &
вывод:
1
Thu 10 Nov 00:26:21 UTC 2016
2
Thu 10 Nov 00:26:24 UTC 2016
3
Thu 10 Nov 00:26:28 UTC 2016
Обратите внимание, что order isn ' т гарантировано. Также вывод не отображается, пока он не закончится (раздражает!). Но даже в этом случае это самый краткий из известных мне способов защиты от одновременного выполнения, не беспокоясь о файлах блокировки, повторных попытках и очистке.
Тем не менее, я хочу обсудить некоторые различные сломанные и частично -работоспособные подходы с использованием ps
и множество предостережений, которые они имеют, поскольку я продолжаю видеть, как люди их используют.
Этот ответ на самом деле является ответом на «Почему бы не использовать ps
и grep
для обработки блокировки в оболочке?»
Во-первых, подход, описанный в другом ответе , который имеет несколько положительных голосов, несмотря на то, что он не (и никогда не мог )работать и явно никогда не тестировался:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
Давайте исправим синтаксические ошибки и неработающие ps
аргументы и получим:
running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
echo Already locked
exit 6
fi
Этот сценарий будет всегда выходить из 6, каждый раз, независимо от того, как вы его запускаете.
Если вы запустите его с ./myscript
, тогда вывод ps
будет просто 12345 -bash
, который не соответствует требуемой строке 12345 bash./myscript
, поэтому это не удастся.
Если вы запустите его с bash myscript
, все станет еще интереснее. Процесс bash разветвляется для запуска конвейера, а дочерняя оболочка запускает ps
и grep
. И исходная оболочка, и дочерняя оболочка будут отображаться в выводе ps
, примерно так:
25793 bash myscript
25795 bash myscript
Это не ожидаемый результат $$ bash $0
, поэтому ваш скрипт завершится.
Теперь, отдавая должное пользователю, написавшему неверный подход #1, я сам сделал что-то подобное, когда впервые попробовал это:
if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
echo >&2 "There are other copies of the script running; exiting."
ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
exit 1
fi
Это почти работает. Но тот факт, что труба разветвляется, отбрасывает это. Так что этот тоже всегда будет выходить.
pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
echo >&2 "There are other copies of this script running; exiting."
ps -fq "${not_this_process//$'\n'/ }"
exit 1
fi
Эта версия позволяет избежать проблемы разветвления конвейера в подходе #2, сначала получая все PID, которые имеют текущий сценарий в своих аргументах командной строки, и , а затем фильтруя этот pidlist отдельно,чтобы опустить PID текущего скрипта.
Это может сработать... при условии, что ни у одного другого процесса нет командной строки, соответствующей $0
, и при условии, что сценарий всегда вызывается одинаково (, например. если он вызывается с относительным путем, а затем с абсолютным путем, последний экземпляр не заметит первого ).
А что, если мы пропустим проверку всей командной строки, поскольку это может не указывать на то, что скрипт действительно запущен, и вместо этого проверим lsof
, чтобы найти все процессы, в которых этот скрипт открыт?
Ну да, такой подход на самом деле не так уж и плох:
if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running. Exiting to avoid conflicts."
ps >&2 -fq "${otherpids//$'\n'/ }"
exit 1
fi
Конечно, если запущена копия сценария, то новый экземпляр запустится нормально, и у вас будут запущены две копии .
Или если работающий скрипт изменен (, например. с Vim или с git checkout
), то «новая» версия скрипта запустится без проблем, так как и Vim, и git checkout
приводят к созданию нового файла (нового индексного дескриптора )вместо Старый.
Однако, если скрипт никогда не модифицировался и никогда не копировался, тогда эта версия довольно хороша. Нет условий гонки, потому что файл скрипта уже должен быть открыт, прежде чем проверка может быть достигнута.
Все еще могут быть ложные срабатывания, если другой процесс имеет открытый файл сценария, но обратите внимание, что даже если он открыт для редактирования в Vim, vim на самом деле не держит файл сценария открытым, поэтому ложных срабатываний не будет.
Но помните, не используйте этот подход, если сценарий может быть отредактирован или скопирован, так как вы получите ложные отрицательные значения , т.е. одновременное выполнение нескольких экземпляров -, так что тот факт, что редактирование с помощью Vim не не давать ложных срабатываний вроде не должно иметь значения для вас. Я упоминаю об этом, потому что подход #3 действительно дает ложные срабатывания (, т.е.отказывается запускаться ), если у вас открыт скрипт в Vim.
лучший -ответ на этот вопрос дает хороший надежный подход.
Возможно, вы можете написать лучший метод... но если вы не понимаете всех проблем и предостережений, связанных со всеми вышеперечисленными подходами, вы вряд ли напишете метод блокировки, который их всех избегает.
lockf()
системный вызов - когда это вернулось все процессы, возобновляется, но только один процесс выиграет блокировку. Никакое состояние состязания. Я не сталкиваюсь с такими проблемами с cronjobs много - противоположное имеет место - но это - проблема, когда это поражает Вас, это имеет потенциал для создания большого количества боли. – maxschlepzig 04.10.2011, 23:15set -o noclobber && echo "$$" > "$lockfile"
получить безопасную нейтрализацию, когда оболочка не поддерживает noclobber опцию. – maxschlepzig 07.10.2011, 12:45noclobber
опция может быть подвержена условиям состязания. См. mywiki.wooledge.org/BashFAQ/045 для некоторой пищи для размышления. – Clint Pachl 16.06.2015, 08:09noclobber
(или-C
) в ksh88 не работает, потому что ksh88 не используетO_EXCL
дляnoclobber
. Если Вы работаете с более новой оболочкой, можно быть в порядке... – jrw32982 supports Monica 17.11.2015, 21:24