Краще рішення, яке не потребує додаткових інструментів.
ln -s my.pid .lock
буде вимагати блокування (супроводжується цим echo $$ > my.pid
), і при відмові можна перевірити, чи PID, що зберігається, .lock
є дійсно активним екземпляром сценарію
Краще рішення, яке не потребує додаткових інструментів.
ln -s my.pid .lock
буде вимагати блокування (супроводжується цим echo $$ > my.pid
), і при відмові можна перевірити, чи PID, що зберігається, .lock
є дійсно активним екземпляром сценарію
Відповіді:
Майже як відповідь nsg: використовуйте каталог замків . Створення каталогів є атомним під Linux та unix та * BSD та безліччю інших ОС.
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, щоб перевірити, чи все-таки виконується процес блокування. На цьому шляху лежать багато умов перегонів.
mkdir
не є атомним щодо NFS (що для мене не так, але я думаю, слід згадати про це, якщо це правда)
/tmp
якому зазвичай не поділяються NFS, так що це повинно бути добре.
rm -rf
для видалення блокування каталогу. rmdir
не вдасться, якщо комусь (не обов'язково вам) вдалося додати файл до каталогу.
Щоб додати відповідь Брюса Едігера і надихнувшись цією відповіддю , ви також повинні додати більше розумних змін до очищення, щоб захистити від припинення сценарію:
#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
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
.
Це може бути занадто спрощеним, будь ласка, виправте мене, якщо я помиляюся. Чи не досить просто ps
?
#!/bin/bash
me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
grep -v $$
. реальні приклади: старий - 14532, новий - 1453, старий - 28858, новий - 858.
grep -v $$
наgrep -v "^${$} "
grep -wv "^$$"
(див. Редагування).
Я б використав файл блокування, як згадував Марко
#!/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
lsof $0
не погано?
$$
файл у замок. Потім sleep
за короткий проміжок часу і прочитайте його назад. Якщо PID все ще залишається вашим, ви успішно придбали замок. Не потребує абсолютно ніяких додаткових інструментів.
Якщо ви хочете переконатися, що працює лише один екземпляр вашого сценарію, подивіться на:
Блокування сценарію (проти паралельного запуску)
Інакше ви можете перевірити 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>
одночасно.
[[(lsof $0 | wc -l) > 2]] && exit
насправді може бути достатньо, чи це також схильне до перегонів?
Ще один спосіб переконатися, що працює один екземпляр bash script:
#!/bin/bash
# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1
...
pidof -o %PPID -x $0
отримує PID існуючого сценарію, якщо його вже запущений або закінчується з кодом помилки 1, якщо інший сценарій не запущений
Хоча ви попросили рішення без додаткових інструментів, це мій улюблений спосіб використання flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
Це випливає з розділу прикладів man flock
, де далі пояснюється:
Це корисний код коробки для скриптів оболонки. Помістіть його у верхній частині сценарію оболонки, який ви хочете заблокувати, і він автоматично заблокує себе під час першого запуску. Якщо env var $ FLOCKER не встановлений для запущеного сценарію оболонки, виконайте flock та захопіть ексклюзивний незаблокувальний замок (використовуючи сам сценарій як файл блокування), перш ніж повторно виконувати себе потрібними аргументами. Він також встановлює FLOCKER env var на потрібне значення, тому він не запускається знову.
Бали, які слід врахувати:
flock
, щоб приклад скрипту закінчувався помилкою, якщо його неможливо знайтиДивіться також /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
Це модифікована версія відповіді Ансельмо . Ідея полягає у створенні дескриптора файлу лише для читання, використовуючи сам скрипт 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"
Основна відмінність від усіх інших відповідей полягає в тому, що цей код не змінює файлову систему, використовує дуже низький слід і не потребує очищення, оскільки дескриптор файлу закритий, як тільки сценарій закінчується незалежно від стану виходу. Таким чином, не має значення, не вдалося чи вдало виконати сценарій.
exec 6< "$SCRIPT"
.
Я використовую cksum, щоб перевірити, чи мій сценарій справді працює єдиним екземпляром, навіть я змінюю ім'я файлу та шлях до файлу .
Я не використовую файл trap & lock, оскільки якщо мій сервер раптово не працює, мені потрібно видалити файл блокування вручну після того, як сервер піднімається.
Примітка: #! / 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
mysum
і fsum
, коли вже не говорите про контрольну суму.
Ви можете скористатися цим: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
Мій код для вас
#!/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
, ви можете помістити весь основний сценарій.