Estou escrevendo um script que detecta se já existe alguma instância dele em execução na minha máquina Linux e mostra na tela o número de instâncias.
O conteúdo do script "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
Ao executar o script ele mostra por tela:
Number of detect_itself.sh instances running now = 2
Second method:
1
Third method:
2
Please, press a key
Mas eu esperava que isso mostrasse:
Number of detect_itself.sh instances running now = 1
Second method:
1
Third method:
1
Please, press a key
Não entendo porque se eu executar ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
ele retorna o valor 1, mas se eu salvar esse valor em uma variável e mostrar com echo ele mostra 2.
Responder1
Isso está acontecendo porque você está executando o ps
comando em um subshell. Quando você executa isso:
INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
Na verdade, isso bifurca um novo subshell para executar esse comando. Como esse fork é uma cópia do pai, agora há duas detect_itself.sh
instâncias em execução. Para ilustrar, execute isto:
#!/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`"
Isso deve imprimir:
$ 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
Felizmente, existe um aplicativo para isso! Esse tipo de coisa é exatamente o motivo pgrep
de existir. Então mude seu script para:
#!/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`
Isso deve imprimir:
$ detect_itself.sh
Number of detect_itself.sh instances running now = 1
Second method:
1
Third method (wrong):
2
IMPORTANTE: isso não é uma coisa segura de se fazer. Por exemplo, se você tiver um script chamado this_will_detect_itself
, isso será contado. se você tiver o arquivo aberto em um editor de texto, isso também será contabilizado. Uma abordagem muito mais robusta para esse tipo de coisa é usar um arquivo de bloqueio. Algo como:
#!/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
Ou, melhor ainda, use trap
para garantir que o arquivo seja removido mesmo quando o script travar. Os detalhes dependerão exatamente do que você deseja fazer e do motivo pelo qual você precisa detectar a instância em execução.