Как удостовериться, что только один экземпляр сценария удара работает?

Вам не нужен expr для этого. Можно использовать конструкции оболочки ${VAR#PATTERN} который расширяется до $VAR с самым коротким префиксом, который соответствует указанному неизолированному шаблону, ${VAR##PATTERN} который разделяет самый длинный префикс, и ${VAR%PATTERN} и ${VAR%%PATTERN} которые разделяют суффиксы.

text='A234321=http://www.example.com/wibble'
protocol=${text%%://*}
url=${protocol##*[!a-z]}://${text#*://}
26
18.09.2012, 14:04
11 ответов

Почти как ответ nsg: используйте каталог блокировки. Создание каталога является атомарным в соответствии с Linux и Unix и *BSD и много других Ose.

if mkdir $LOCKDIR
then
    # Do important, exclusive stuff
    if rmdir $LOCKDIR
    then
        echo "Victory is mine"
    else
        echo "Could not remove lock dir" >&2
    fi
else
    # Handle error condition
    ...
fi

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

18
27.01.2020, 19:40
  • 1
    , который я рассмотрел бы использование сохраненного PID, чтобы проверить, жив ли экземпляр блокировки все еще. Однако вот является требование этим mkdir не является атомарным на NFS (который не имеет место для меня, но я предполагаю, что нужно упомянуть, что, если верный), –  Tobias Kienzler 18.09.2012, 16:08
  • 2
    Да, любой ценой используйте сохраненный PID, чтобы видеть, выполняется ли процесс блокировки все еще, но не пытайтесь сделать что-либо кроме журнала сообщение. Работа проверки сохраненного pid, создания нового файла PID, и т.д., оставляет большое окно для гонок. –  Bruce Ediger 18.09.2012, 16:37
  • 3
    Хорошо, как Ihunath заявил, lockdir, скорее всего, будет в /tmp который обычно является не совместно использованным NFS, так, чтобы был прекрасен. –  Tobias Kienzler 19.09.2012, 11:33
  • 4
    я использовал бы rm -rf удалить каталог блокировки. rmdir перестанет работать, если кому-то (не обязательно Вы) удалось добавить файл к каталогу. –  chepner 22.09.2012, 07:32

Это может быть слишком упрощенно, исправьте меня, если я неправ. Не простое ps достаточно?

#!/bin/bash 

me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;

# do stuff below this comment
6
27.01.2020, 19:40
  • 1
    Хороший и/или блестящий. :) –  Spooky 03.03.2017, 18:49
  • 2
    я использовал это условие в течение недели, и в 2 случаях, которые это не препятствовало тому, чтобы новый процесс запустил. Я изобразил то, что проблема - новый pid является подстрокой старой и скрыт grep -v $$. реальные примеры: старый - 14532, новый - 1453, старый - 28858, новый - 858. –  Naktibalda 22.02.2018, 13:30
  • 3
    я зафиксировал его путем изменения grep -v $$ кому: grep -v "^${$} " –  Naktibalda 22.02.2018, 13:52
  • 4
    @Naktibalda хорошая выгода, Спасибо! Вы могли также зафиксировать его с grep -wv "^$$" (см. редактирование). –  terdon♦ 22.02.2018, 14:38
  • 5
    Спасибо за то обновление. Мой шаблон иногда перестал работать, потому что короче pids оставили заполненными с пробелами. –  Naktibalda 08.03.2018, 18:50

Я использовал бы файл блокировки, как упомянуто Marco

#!/bin/bash

# Exit if /tmp/lock.file exists
[ -f /tmp/lock.file ] && exit

# Create lock file, sleep 1 sec and verify lock
echo $$ > /tmp/lock.file
sleep 1
[ "x$(cat /tmp/lock.file)" == "x"$$ ] || exit

# Do stuff
sleep 60

# Remove lock file
rm /tmp/lock.file
4
27.01.2020, 19:40
  • 1
    (я думаю, что Вы забыли создавать файл блокировки) Что относительно условий состязания? –  Tobias Kienzler 18.09.2012, 14:28
  • 2
    :) Да, условия состязания проблема в моем примере, я обычно пишу почасовые или ежедневные задания крона, и условия состязания редки. –  nsg 18.09.2012, 14:32
  • 3
    Они не должны быть релевантными в моем случае также, но это - что-то, что нужно иметь в виду. Возможно, использование lsof $0 не плохо, также? –  Tobias Kienzler 18.09.2012, 14:34
  • 4
    Можно уменьшить состояние состязания путем записи Вашего $$ в файле блокировки. Затем sleep для короткого интервала и читают его назад. Если PID является все еще Вашим, Вы успешно получили блокировку. Не нуждается в абсолютно никаких дополнительных инструментах. –  manatwork 18.09.2012, 14:41
  • 5
    я никогда не использовал lsof с этой целью, я это, он должен работать. Обратите внимание, что lsof является действительно медленным в моей системе (1-2 секунды) и скорее всего существует много времени для условий состязания. –  nsg 18.09.2012, 14:45

Если Вы хотите удостовериться, что только один экземпляр Вашего сценария работает, смотрят на:

Заблокируйте свой сценарий (против выполненной параллели)

Иначе можно проверить ps или вызовите lsof <full-path-of-your-script>, так как я не назвал бы их дополнительными инструментами.


Дополнение:

на самом деле я думал о выполнении его как это:

for LINE in `lsof -c <your_script> -F p`; do 
    if [ $$ -gt ${LINE#?} ] ; then
        echo "'$0' is already running" 1>&2
        exit 1;
    fi
done

это гарантирует что только процесс с самым низким pid продолжает работать даже если Вы ветвление-и-должностное-лицо несколько экземпляров <your_script> одновременно.

3
27.01.2020, 19:40
  • 1
    Спасибо за ссылку, но Вы могли включать основные части в свой ответ? Это - общая политика в SE для предотвращения гнили ссылки... Но что-то как [[(lsof $0 | wc -l) > 2]] && exit мог бы на самом деле быть достаточно, или это также подвержено условиям состязания? операция в секунду –  Tobias Kienzler 18.09.2012, 14:30
  • 2
    Вы правы, что основная часть моего ответа пропускала и только отправляла ссылки, является довольно хромым. Я добавил свое собственное предложение к ответу. –  user1146332 18.09.2012, 15:52

Чтобы добавить в Ответ Ответ Bruce Ediger , и вдохновлен Этот ответ Этот ответ , вы также должны добавить больше умных для очистки для защиты от скрипта:

#Remove the lock directory
function cleanup {
    if rmdir $LOCKDIR; then
        echo "Finished"
    else
        echo "Failed to remove lock directory '$LOCKDIR'"
        exit 1
    fi
}

if mkdir $LOCKDIR; then
    #Ensure that if we "grabbed a lock", we release it
    #Works for SIGTERM and SIGINT(Ctrl-C)
    trap "cleanup" EXIT

    echo "Acquired lock, running"

    # Processing starts here
else
    echo "Could not create lock directory '$LOCKDIR'"
    exit 1
fi
18
27.01.2020, 19:40

Хотя вы просили найти решение без дополнительных инструментов, это мой любимый способ использования flock :

#!/bin/sh

[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :

echo "servus!"
sleep 10

Это взято из раздела примеров man flock , в котором дополнительно объясняется:

Т это полезный шаблонный код для сценариев оболочки. Поместите его в начало сценария оболочки, который вы хотите заблокировать, и он автоматически заблокируется при первом запуске. Если env var $ FLOCKER не настроен на запускаемый сценарий оболочки, выполните flock и захватите исключительную неблокирующую блокировку (используя сам сценарий в качестве файла блокировки) перед повторным запуском с правильными аргументами. Он также устанавливает правильное значение переменной env FLOCKER, чтобы она больше не запускалась.

На что следует обратить внимание:

  • Требуется flock , пример сценария завершается с ошибкой, если он не может быть найден
  • Не требует дополнительного файла блокировки
  • Может не работать, если сценарий находится в NFS (см. https://serverfault.com/questions/66919/file-locks-on-an-nfs )

См. также https://stackoverflow.com/questions / 185451 / быстрый-и-грязный-способ-обеспечить-только-один-экземпляр-сценария-оболочки-работает-на .

3
27.01.2020, 19:40

Это модифицированная версия Ответа Ансельмо . Идея состоит в том, чтобы создать файловый дескриптор только для чтения с помощью самого сценария bash и использовать flockдля обработки блокировки.

script=`realpath $0`     # get absolute path to the script itself
exec 6< "$script"        # open bash script using file descriptor 6
flock -n 6 || { echo "ERROR: script is already running" && exit 1; }   # lock file descriptor 6 OR show error message if script is already running

echo "Run your single instance code here"

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

4
27.01.2020, 19:40

Мой код тебе

#!/bin/bash

script_file="$(/bin/readlink -f $0)"
lock_file=${script_file////_}

function executing {
  echo "'${script_file}' already executing"
  exit 1
}

(
  flock -n 9 || executing

  sleep 10

) 9> /var/lock/${lock_file}

На основе man flock, только улучшение:

  • имя файла блокировки, основанное на полном имени скрипта
  • сообщениеexecuting

Там, где я поставил здесь sleep 10, можно поставить все основные скрипты.

0
27.01.2020, 19:40

Я использую cksum для проверки того, что мой скрипт действительно работает в одном экземпляре, даже если я меняю имя файла и путь к файлу .

Я не использую файл ловушки и блокировки, потому что, если мой сервер внезапно выйдет из строя,Мне нужно вручную удалить файл блокировки после запуска сервера.

Примечание:#!/bin/bash в первой строке требуется для grep ps

#!/bin/bash

checkinstance(){
   nprog=0
   mysum=$(cksum $0|awk '{print $1}')
   for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do 
        proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash) 
        if [[ $? -eq 0 ]];then 
           cmd=$(strings /proc/$i/cmdline|grep -v bash)
                if [[ $? -eq 0 ]];then 
                   fsum=$(cksum /proc/$i/cwd/$cmd|awk '{print $1}')
                   if [[ $mysum -eq $fsum ]];then
                        nprog=$(($nprog+1))
                   fi
                fi
        fi
   done

   if [[ $nprog -gt 1 ]];then
        echo $0 is already running.
        exit
   fi
}

checkinstance 

#--- run your script bellow 

echo pass
while true;do sleep 1000;done

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

#!/bin/bash

mysum=1174212411

checkinstance(){
   nprog=0
   for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do 
        proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash) 
        if [[ $? -eq 0 ]];then 
           cmd=$(strings /proc/$i/cmdline|grep -v bash)
                if [[ $? -eq 0 ]];then 
                   fsum=$(grep mysum /proc/$i/cwd/$cmd|head -1|awk -F= '{print $2}')
                   if [[ $mysum -eq $fsum ]];then
                        nprog=$(($nprog+1))
                   fi
                fi
        fi
   done

   if [[ $nprog -gt 1 ]];then
        echo $0 is already running.
        exit
   fi
}

checkinstance

#--- run your script bellow

echo pass
while true;do sleep 1000;done
1
27.01.2020, 19:40

Этот удобный пакет делает то, что вы ищете.

https://github.com/krezreb/singleton

после установки просто добавьте к команде префиксsingleton LOCKNAME

напр.singleton LOCKNAME PROGRAM ARGS...

0
19.02.2020, 17:07

Самый простой сценарий с одним вкладышем вместо написания сложных операторов PID или блокировки

flock -xn LOCKFILE.lck -c SCRIPT.SH

где x обозначает эксклюзивную блокировку а n обозначает не блокировку -, которая терпит неудачу, а не ожидает освобождения блокировки. c запускает сценарий, который вы хотите запустить.

0
13.05.2020, 21:01

Теги

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