@ jw013 obrigado! Então, talvez algo como ln -s my.pid .lockvai reivindicar o bloqueio (seguido echo $$ > my.pid) e em caso de falha pode verificar se o PID armazenado em .locké realmente uma instância ativa do script
Tobias KIENZLER
Respostas:
18
Quase como a resposta do nsg: use um diretório de bloqueio . A criação de diretórios é atômica sob linux e unix e * BSD e muitos outros sistemas operacionais.
if mkdir $LOCKDIR
then# Do important, exclusive stuffif rmdir $LOCKDIR
then
echo "Victory is mine"else
echo "Could not remove lock dir">&2fielse# Handle error condition...fi
Você pode colocar o PID do bloqueio sh em um arquivo no diretório de bloqueio para fins de depuração, mas não caia na armadilha de pensar que pode verificar esse PID para ver se o processo de bloqueio ainda é executado. Muitas condições de corrida se encontram nesse caminho.
Eu consideraria usar o PID armazenado para verificar se a instância de bloqueio ainda está ativa. No entanto, aqui é uma reivindicação que mkdirnão é atômica sobre NFS (que não é o caso para mim, mas eu acho que deve-se mencionar que, se for verdade)
Tobias KIENZLER
Sim, use o PID armazenado para verificar se o processo de bloqueio ainda é executado, mas não tente fazer nada além de registrar uma mensagem. O trabalho de verificar o pid armazenado, criar um novo arquivo PID, etc, deixa uma grande janela para as corridas.
precisa saber é o seguinte
Ok, como Ihunath afirmou , o lockdir provavelmente estaria no /tmpqual geralmente não é compartilhado pelo NFS, portanto tudo bem.
Tobias Kienzler 19/09/12
Eu usaria rm -rfpara remover o diretório de bloqueio. rmdirfalhará se alguém (não necessariamente você) conseguir adicionar um arquivo ao diretório.
chepner
18
Para adicionar à resposta de Bruce Ediger , e inspirado nessa resposta , você também deve adicionar mais inteligência à limpeza para se proteger contra o encerramento de scripts:
#Remove the lock directoryfunction cleanup {if rmdir $LOCKDIR;then
echo "Finished"else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1fi}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 hereelse
echo "Could not create lock directory '$LOCKDIR'"
exit 1fi
Eu usei essa condição por uma semana e, em duas ocasiões, não impediu o início de um novo processo. Imaginei qual era o problema - o novo pid é uma subcadeia do antigo e fica oculto grep -v $$. exemplos reais: antigo - 14532, novo - 1453, antigo - 28858, novo - 858.
Naktibalda
Eu fixo-lo, mudando grep -v $$paragrep -v "^${$} "
Naktibalda
@Naktibalda boa captura, obrigado! Você também pode corrigi-lo com grep -wv "^$$"(veja editar).
terdon
Obrigado por essa atualização. Ocasionalmente, meu padrão falhava porque os pids mais curtos eram preenchidos com espaços.
Naktibalda 8/03
4
Eu usaria um arquivo de bloqueio, como mencionado por Marco
(Acho que você esqueceu de criar o arquivo de bloqueio) E as condições da corrida ?
Tobias Kienzler
ops :) Sim, as condições de corrida são um problema no meu exemplo, eu normalmente escrevo trabalhos cron de hora em hora ou diariamente e as condições de corrida são raras.
nsg 18/09/12
Eles também não devem ser relevantes no meu caso, mas é algo que se deve ter em mente. Talvez usar lsof $0não seja ruim, também?
Tobias Kienzler
Você pode diminuir a condição de corrida escrevendo seu $$no arquivo de bloqueio. Depois, sleeppor um curto intervalo e leia-o novamente. Se o PID ainda for seu, você adquiriu o bloqueio com sucesso. Não precisa absolutamente de ferramentas adicionais.
manatwork
11
Eu nunca usei lsof para esse fim, e isso deve funcionar. Observe que lsof é realmente lento no meu sistema (1 a 2 segundos) e provavelmente há muito tempo para as condições da corrida.
nsg 18/09/12
3
Se você deseja garantir que apenas uma instância do seu script esteja em execução, consulte:
Caso contrário, você pode verificar psou invocar lsof <full-path-of-your-script>, pois eu não os chamaria de ferramentas adicionais.
Suplemento :
na verdade, eu pensei em fazê-lo assim:
for LINE in`lsof -c <your_script> -F p`;doif[ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running"1>&2
exit 1;fidone
isso garante que apenas o processo com o menor pidcontinue em execução, mesmo se você forçar e executar várias instâncias <your_script>simultaneamente.
Obrigado pelo link, mas você poderia incluir as partes essenciais em sua resposta? É política comum no SE impedir a podridão do link ... Mas algo como isso [[(lsof $0 | wc -l) > 2]] && exitpode ser suficiente, ou isso também é propenso a condições de corrida?
Tobias Kienzler
Você está certo, a parte essencial da minha resposta estava faltando e apenas a publicação de links é muito ruim. Eu adicionei minha própria sugestão à resposta.
user1146332
3
Outra maneira de garantir que uma única instância do script bash seja executada:
#!/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 obtém o PID do script existente se ele já estiver em execução ou sai com o código de erro 1 se nenhum outro script estiver em execução
Isso vem da seção de exemplos man flock, que explica ainda:
Esse é um código útil para scripts de shell. Coloque-o na parte superior do script de shell que você deseja bloquear e ele se bloqueará automaticamente na primeira execução. Se o env var $ FLOCKER não estiver definido como o shell script que está sendo executado, execute o flock e pegue um bloqueio exclusivo sem bloqueio (usando o próprio script como arquivo de bloqueio) antes de se executar novamente com os argumentos corretos. Ele também define o env var FLOCKER para o valor certo, para que não seja executado novamente.
Pontos a considerar:
Requer flock, o script de exemplo termina com um erro se não puder ser encontrado
Esta é uma versão modificada da resposta de Anselmo . A idéia é criar um descritor de arquivo somente leitura usando o próprio script bash e use flockpara manipular o bloqueio.
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"
A principal diferença para todas as outras respostas é que esse código não modifica o sistema de arquivos, usa uma área muito baixa e não precisa de nenhuma limpeza, pois o descritor de arquivo é fechado assim que o script termina independentemente do estado de saída. Portanto, não importa se o script falhar ou for bem-sucedido.
Você deve sempre citar todas as referências de variáveis do shell, a menos que tenha um bom motivo para não fazê- lo e tenha certeza de que sabe o que está fazendo. Então você deveria estar fazendo exec 6< "$SCRIPT".
Scott
@ Scott Alterei o código de acordo com suas sugestões. Muito Obrigado.
31419 John Doe
1
Estou usando o cksum para verificar se meu script está realmente executando uma instância única, mesmo que eu mude o nome do arquivo e o caminho do arquivo .
Não estou usando o arquivo de interceptação e bloqueio, porque se meu servidor estiver inoperante repentinamente, preciso remover manualmente o arquivo de bloqueio após a inicialização do servidor.
Nota: #! / Bin / bash na primeira linha é necessário para grep ps
Explique exatamente como codificar a soma de verificação é uma boa ideia.
Scott
não codificando a soma de verificação, ela apenas cria a chave de identidade do seu script; quando outra instância estiver em execução, ela verificará outro processo de script de shell e classificará o arquivo primeiro, se a sua chave de identidade estiver nesse arquivo, significa que a instância já está em execução.
arputra
ESTÁ BEM; por favor edite sua resposta para explicar isso. E, no futuro, não publique vários blocos de código de 30 linhas que parecem (quase) idênticos sem dizer e explicar como são diferentes. E não diga coisas como “você pode codificar [sic] cksum dentro de seu script” e não continue usando nomes de variáveis mysume fsum, quando não estiver mais falando sobre uma soma de verificação.
Scott
Parece interessante, obrigado! E bem-vindo ao unix.stackexchange :)
ln -s my.pid .lock
vai reivindicar o bloqueio (seguidoecho $$ > my.pid
) e em caso de falha pode verificar se o PID armazenado em.lock
é realmente uma instância ativa do scriptRespostas:
Quase como a resposta do nsg: use um diretório de bloqueio . A criação de diretórios é atômica sob linux e unix e * BSD e muitos outros sistemas operacionais.
Você pode colocar o PID do bloqueio sh em um arquivo no diretório de bloqueio para fins de depuração, mas não caia na armadilha de pensar que pode verificar esse PID para ver se o processo de bloqueio ainda é executado. Muitas condições de corrida se encontram nesse caminho.
fonte
mkdir
não é atômica sobre NFS (que não é o caso para mim, mas eu acho que deve-se mencionar que, se for verdade)/tmp
qual geralmente não é compartilhado pelo NFS, portanto tudo bem.rm -rf
para remover o diretório de bloqueio.rmdir
falhará se alguém (não necessariamente você) conseguir adicionar um arquivo ao diretório.Para adicionar à resposta de Bruce Ediger , e inspirado nessa resposta , você também deve adicionar mais inteligência à limpeza para se proteger contra o encerramento de scripts:
fonte
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
,.Isso pode ser muito simplista, corrija-me se estiver errado. Não é simples o
ps
suficiente?fonte
grep -v $$
. exemplos reais: antigo - 14532, novo - 1453, antigo - 28858, novo - 858.grep -v $$
paragrep -v "^${$} "
grep -wv "^$$"
(veja editar).Eu usaria um arquivo de bloqueio, como mencionado por Marco
fonte
lsof $0
não seja ruim, também?$$
no arquivo de bloqueio. Depois,sleep
por um curto intervalo e leia-o novamente. Se o PID ainda for seu, você adquiriu o bloqueio com sucesso. Não precisa absolutamente de ferramentas adicionais.Se você deseja garantir que apenas uma instância do seu script esteja em execução, consulte:
Bloqueie seu script (contra execução paralela)
Caso contrário, você pode verificar
ps
ou invocarlsof <full-path-of-your-script>
, pois eu não os chamaria de ferramentas adicionais.Suplemento :
na verdade, eu pensei em fazê-lo assim:
isso garante que apenas o processo com o menor
pid
continue em execução, mesmo se você forçar e executar várias instâncias<your_script>
simultaneamente.fonte
[[(lsof $0 | wc -l) > 2]] && exit
pode ser suficiente, ou isso também é propenso a condições de corrida?Outra maneira de garantir que uma única instância do script bash seja executada:
pidof -o %PPID -x $0
obtém o PID do script existente se ele já estiver em execução ou sai com o código de erro 1 se nenhum outro script estiver em execuçãofonte
Embora você tenha solicitado uma solução sem ferramentas adicionais, esta é a minha maneira favorita de usar
flock
:Isso vem da seção de exemplos
man flock
, que explica ainda:Pontos a considerar:
flock
, o script de exemplo termina com um erro se não puder ser encontradoConsulte também /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
fonte
Esta é uma versão modificada da resposta de Anselmo . A idéia é criar um descritor de arquivo somente leitura usando o próprio script bash e use
flock
para manipular o bloqueio.A principal diferença para todas as outras respostas é que esse código não modifica o sistema de arquivos, usa uma área muito baixa e não precisa de nenhuma limpeza, pois o descritor de arquivo é fechado assim que o script termina independentemente do estado de saída. Portanto, não importa se o script falhar ou for bem-sucedido.
fonte
exec 6< "$SCRIPT"
.Estou usando o cksum para verificar se meu script está realmente executando uma instância única, mesmo que eu mude o nome do arquivo e o caminho do arquivo .
Não estou usando o arquivo de interceptação e bloqueio, porque se meu servidor estiver inoperante repentinamente, preciso remover manualmente o arquivo de bloqueio após a inicialização do servidor.
Nota: #! / Bin / bash na primeira linha é necessário para grep ps
Ou você pode codificar cksum no seu script para não se preocupar novamente se quiser alterar o nome do arquivo, o caminho ou o conteúdo do script .
fonte
mysum
efsum
, quando não estiver mais falando sobre uma soma de verificação.Você pode usar isso: https://github.com/sayanarijit/pidlock
fonte
Meu código para você
Com base em
man flock
, melhorando apenas:executing
Onde eu coloquei aqui o
sleep 10
, você pode colocar todo o script principal.fonte