Быстрый-и-грязный способ гарантировать только один экземпляр сценария оболочки работает за один раз

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

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals для гарантированного непустого объекта.

Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null.

Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

174
задан codeforester 22 February 2019 в 04:35
поделиться

12 ответов

Вот реализация, которая использует lockfile и повторяет PID в него. Это служит защитой, если процесс уничтожается прежде, чем удалить pidfile:

LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
    echo "already running"
    exit
fi

# make sure the lockfile is removed when we exit and then claim it
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $ > ${LOCKFILE}

# do stuff
sleep 1000

rm -f ${LOCKFILE}

прием здесь kill -0, который не поставляет сигнала, но просто проверяет, существует ли процесс с данным PID. Также вызов к trap гарантирует, что lockfile удален, даже когда Ваш процесс уничтожается (кроме kill -9).

102
ответ дан Edson Medina 23 November 2019 в 20:27
поделиться

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

0
ответ дан I GIVE CRAP ANSWERS 23 November 2019 в 20:27
поделиться

Быстрый и грязный?

#!/bin/sh

if [ -f sometempfile ]
  echo "Already running... will now terminate."
  exit
else
  touch sometempfile
fi

..do what you want here..

rm sometempfile
0
ответ дан glenn jackman 23 November 2019 в 20:27
поделиться

PID и lockfiles являются определенно самыми надежными. Когда Вы пытаетесь запустить программу, она может проверить на lockfile, который и если она существует, она может использовать ps, чтобы видеть, работает ли процесс все еще. Если это не, сценарий может запуститься, обновив PID в lockfile к его собственному.

1
ответ дан Drew Stephens 23 November 2019 в 20:27
поделиться

Используйте flock(1) для создания эксклюзивной ограниченной по объему блокировки на дескрипторе файла. Таким образом, можно даже синхронизировать различные части сценария.

#!/bin/bash

(
  # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
  flock -x -w 10 200 || exit 1

  # Do stuff

) 200>/var/lock/.myscript.exclusivelock

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

Протест: эта конкретная команда является частью util-linux . При выполнении операционной системы кроме Linux это может или не может быть доступно.

204
ответ дан Palec 23 November 2019 в 20:27
поделиться

Существует обертка вокруг скопления (2), названный системный вызов, лишено воображения, скапливается (1). Это делает относительно легким надежно получить монопольные блокировки, не вызывая беспокойство об очистке и т.д. существуют примеры на страница справочника относительно того, как использовать его в сценарии оболочки.

42
ответ дан Cowan 23 November 2019 в 20:27
поделиться

Для создания блокировки надежной, Вам нужна атомарная операция. Многие вышеупомянутые предложения не являются атомарными. Предложенный lockfile (1) утилита выглядит многообещающим как упомянутая страница справочника, что его "стойкое к NFS". Если Ваша ОС не поддерживает lockfile (1), и Ваше решение должно работать над NFS, у Вас нет многих опций....

NFSv2 начинает две атомарных операции:

  • символьная ссылка
  • переименовывает

С NFSv3, создать вызов является также атомарным.

операции Каталога НЕ являются атомарными под NFSv2 и NFSv3 (отошлите к книге 'NFS, Проиллюстрированный' Brent Callaghan, ISBN 0-201-32570-5; Brent является ветераном NFS в Sun).

Знание этого, можно реализовать спин-блокировки для файлов и каталогов (в оболочке, не PHP):

блокируют текущий dir:

while ! ln -s . lock; do :; done

блокируют файл:

while ! ln -s ${f} ${f}.lock; do :; done

разблокировали текущий dir (предположение, рабочий процесс действительно получил блокировку):

mv lock deleteme && rm deleteme

разблокировали файл (предположение, рабочий процесс действительно получил блокировку):

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme

Удаляют, является также не атомарным, поэтому сначала переименовывание (который является атомарным), и затем удалять.

Для символьной ссылки и переименовывают вызовы, оба имен файлов должны находиться в той же файловой системе. Мое предложение: используйте только простые имена файлов (никакие пути) и поместите файл и блокировку в тот же каталог.

24
ответ дан 23 November 2019 в 20:27
поделиться

Некоторые unixes имеют lockfile , который уже очень похож на упомянутый flock .

Из страницы справочника:

lockfile может использоваться для создания одного или нескольких семафорных файлов. Если блокировка - файл не может создать все указанные файлы (в указанном порядке), это ожидает sleeptime (значения по умолчанию к 8) секунды и повторяет последний файл, который не успешно выполнялся. Можно определить количество повторений, чтобы сделать, пока отказ не возвращается. Если количество повторений-1 (значение по умолчанию, т.е.-r-1) lockfile повторит навсегда.

3
ответ дан dmckee 23 November 2019 в 20:27
поделиться

Создайте файл блокировки в известном месте, и проверка на существование на сценарии запускаются? Помещение PID в файле могло бы быть полезным, если чья-то попытка разыскать ошибочный экземпляр это предотвращает выполнение сценария.

5
ответ дан Rob 23 November 2019 в 20:27
поделиться

Все подходы, которые проверяют существование «файлов блокировки», ошибочны.

Почему? Потому что нет способа проверить, существует ли файл и создать его в одном атомарном действии. Из-за этого; есть условие гонки: БУДЕТ делать ваши попытки разрыва взаимного исключения.

Вместо этого вам нужно использовать mkdir . mkdir создает каталог, если он еще не существует, и если он существует, он устанавливает код выхода. Что еще более важно, он делает все это в одном атомарном действии, что делает его идеальным для этого сценария.

if ! mkdir /tmp/myscript.lock 2>/dev/null; then
    echo "Myscript is already running." >&2
    exit 1
fi

Подробнее см. Превосходный BashFAQ: http://mywiki.wooledge.org/BashFAQ/045

Если вы хотите позаботиться об устаревших замках, подойдет fuser (1) . Единственным недостатком здесь является то, что операция занимает около секунды,

155
ответ дан Derek Mahar 23 November 2019 в 20:27
поделиться

Я считаю, что для машины Debian хорошим решением будет пакет lockfile-progs . procmail также поставляется с инструментом lockfile . Однако иногда я не могу ничего из этого сделать.

Вот мое решение, которое использует mkdir для атомарности и файл PID для обнаружения устаревших блокировок. Этот код в настоящее время работает на Cygwin и работает хорошо.

Чтобы использовать его, просто вызовите exclusive_lock_require , когда вам нужен эксклюзивный доступ к чему-либо. Необязательный параметр имени блокировки позволяет разделять блокировки между разными сценариями. Также есть две функции нижнего уровня ( exclusive_lock_try и exclusive_lock_retry ), если вам понадобится что-то более сложное.

function exclusive_lock_try() # [lockname]
{

    local LOCK_NAME="${1:-`basename $0`}"

    LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
    local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"

    if [ -e "$LOCK_DIR" ]
    then
        local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
        if [ ! -z "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2> /dev/null
        then
            # locked by non-dead process
            echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
            return 1
        else
            # orphaned lock, take it over
            ( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && local LOCK_PID="$$"
        fi
    fi
    if [ "`trap -p EXIT`" != "" ]
    then
        # already have an EXIT trap
        echo "Cannot get lock, already have an EXIT trap"
        return 1
    fi
    if [ "$LOCK_PID" != "$$" ] &&
        ! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 && echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
    then
        local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
        # unable to acquire lock, new process got in first
        echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
        return 1
    fi
    trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT

    return 0 # got lock

}

function exclusive_lock_retry() # [lockname] [retries] [delay]
{

    local LOCK_NAME="$1"
    local MAX_TRIES="${2:-5}"
    local DELAY="${3:-2}"

    local TRIES=0
    local LOCK_RETVAL

    while [ "$TRIES" -lt "$MAX_TRIES" ]
    do

        if [ "$TRIES" -gt 0 ]
        then
            sleep "$DELAY"
        fi
        local TRIES=$(( $TRIES + 1 ))

        if [ "$TRIES" -lt "$MAX_TRIES" ]
        then
            exclusive_lock_try "$LOCK_NAME" > /dev/null
        else
            exclusive_lock_try "$LOCK_NAME"
        fi
        LOCK_RETVAL="${PIPESTATUS[0]}"

        if [ "$LOCK_RETVAL" -eq 0 ]
        then
            return 0
        fi

    done

    return "$LOCK_RETVAL"

}

function exclusive_lock_require() # [lockname] [retries] [delay]
{
    if ! exclusive_lock_retry "$@"
    then
        exit 1
    fi
}
4
ответ дан 23 November 2019 в 20:27
поделиться

Вам нужна атомарная операция, например flock, иначе это в конечном итоге не удастся.

Но что делать, если стая отсутствует. Ну есть mkdir. Это тоже атомарная операция. Только один процесс приведет к успешному выполнению mkdir, все остальные завершатся ошибкой.

Итак, код такой:

if mkdir /var/lock/.myscript.exclusivelock
then
  # do stuff
  :
  rmdir /var/lock/.myscript.exclusivelock
fi

Вам нужно позаботиться о устаревших блокировках, иначе после сбоя ваш скрипт больше не запустится.

27
ответ дан 23 November 2019 в 20:27
поделиться