Я пишу скрипт, который определяет, запущен ли какой-либо экземпляр на моей машине Linux, и выводит на экран количество экземпляров.
Содержимое скрипта «detect_itself.sh» следующее:
#!/bin/sh
INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
echo "Number of detect_itself.sh instances running now =" $INSTANCES_NUMBER
echo "Second method:"
ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
echo "Third method:"
echo `ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
echo "Please, press a key"
read -r key
При выполнении скрипта на экране отображается:
Number of detect_itself.sh instances running now = 2
Second method:
1
Third method:
2
Please, press a key
Но я ожидал увидеть:
Number of detect_itself.sh instances running now = 1
Second method:
1
Third method:
1
Please, press a key
Я не понимаю, почему при выполнении ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
возвращается значение 1, а если сохранить это значение в переменной и показать с помощью echo, возвращается значение 2.
решение1
Это происходит потому, что вы запускаете ps
команду в подоболочке. Когда вы запускаете это:
INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
Это фактически разветвляет новую подоболочку для запуска этой команды. Поскольку эта разветвленная оболочка является копией родительской, теперь запущено два detect_itself.sh
экземпляра. Для иллюстрации запустите это:
#!/bin/sh
echo "Running the ps command directly:"
ps -ef | grep detect_itself.sh | grep -v -i grep
echo "Running the ps command in a subshell:"
echo "`ps -ef | grep detect_itself.sh | grep -v -i grep`"
Это должно вывести:
$ test.sh
Running the ps command directly:
terdon 25683 24478 0 14:58 pts/11 00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
Running the ps command in a subshell:
terdon 25683 24478 0 14:58 pts/11 00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
terdon 25688 25683 0 14:58 pts/11 00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
К счастью, для этого есть приложение! Именно для этого и существуют такие вещи pgrep
. Так что измените свой скрипт на:
#!/bin/sh
instances=`pgrep -fc detect_itself.sh`
echo "Number of detect_itself.sh instances running now = $instances"
echo "Second method:"
ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
echo "Third method (wrong):"
echo `ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
Это должно вывести:
$ detect_itself.sh
Number of detect_itself.sh instances running now = 1
Second method:
1
Third method (wrong):
2
ВАЖНЫЙ: это небезопасно. Например, если у вас есть скрипт с именем this_will_detect_itself
, он будет засчитан. Если у вас есть файл, открытый в текстовом редакторе, он также будет засчитан. Гораздо более надежный подход для такого рода вещей — использовать файл блокировки. Что-то вроде:
#!/bin/sh
if [[ -e /tmp/I_am_running ]]; then
echo "Already running! Will exit."
exit
else
touch /tmp/I_am_running
fi
## do whatever you want to do here
## remove the lock file at the end
rm /tmp/I_am_running
Или, что еще лучше, рассмотрите использование trap
, чтобы убедиться, что файл удаляется даже при сбое скрипта. Детали будут зависеть от того, что именно вы хотите сделать, от того, почему вам нужно обнаружить запущенный экземпляр.