No entanto, quero discutir algumas abordagens quebradas e semi-viáveis usando ps
e as muitas advertências que elas têm, já que continuo vendo as pessoas usá-las.
Esta resposta é realmente a resposta para "Por que não usar ps
e grep
manipular o bloqueio no shell?"
Abordagem quebrada # 1
Primeiro, uma abordagem dada em outra resposta que tem alguns votos positivos, apesar de não funcionar (e nunca poderia) funcionar e claramente nunca ter sido testada:
running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do
echo Already locked
exit 6
fi
Vamos corrigir os erros de sintaxe e os ps
argumentos quebrados e obter:
running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
echo Already locked
exit 6
fi
Esse script sempre sai do 6, toda vez, não importa como você o execute.
Se você executá-lo ./myscript
, a ps
saída será apenas 12345 -bash
, o que não corresponde à string necessária 12345 bash ./myscript
, e isso falhará.
Se você executá-lo bash myscript
, as coisas ficam mais interessantes. O processo do bash bifurca-se para executar o pipeline e o shell filho executa o ps
e grep
. O shell original e o shell filho aparecerão na ps
saída, algo como isto:
25793 bash myscript
25795 bash myscript
Essa não é a saída esperada $$ bash $0
; portanto, seu script será encerrado.
Abordagem quebrada # 2
Agora, com toda a justiça para o usuário que escreveu a abordagem quebrada nº 1, fiz algo parecido quando tentei isso pela primeira vez:
if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
echo >&2 "There are other copies of the script running; exiting."
ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
exit 1
fi
Isso quase funciona. Mas o fato de bifurcar-se para passar o tubo acaba com isso. Portanto, este sempre sairá também.
Abordagem não confiável # 3
pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
echo >&2 "There are other copies of this script running; exiting."
ps -fq "${not_this_process//$'\n'/ }"
exit 1
fi
Essa versão evita o problema de bifurcação de pipeline na abordagem nº 2, obtendo primeiro todos os PIDs que possuem o script atual em seus argumentos de linha de comando e, em seguida, filtrando essa lista de pidlists separadamente para omitir o PID do script atual.
Isso pode funcionar ... desde que nenhum outro processo tenha uma linha de comando correspondente ao $0
, e fornecendo o script sempre chamado da mesma maneira (por exemplo, se for chamado com um caminho relativo e depois um caminho absoluto, a última instância não notará o anterior )
Abordagem não confiável # 4
E se ignorarmos a verificação da linha de comando completa, pois isso pode não indicar um script realmente em execução e, em lsof
vez disso, verificarmos todos os processos que têm esse script aberto?
Bem, sim, essa abordagem não é realmente tão ruim:
if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running. Exiting to avoid conflicts."
ps >&2 -fq "${otherpids//$'\n'/ }"
exit 1
fi
Obviamente, se uma cópia do script estiver em execução, a nova instância será iniciada muito bem e você terá duas cópias em execução.
Ou se o script em execução for modificado (por exemplo, com Vim ou com a git checkout
), a versão "nova" do script será iniciada sem problemas, pois o Vim e git checkout
resulta em um novo arquivo (um novo inode) no lugar do antigo.
No entanto, se o script nunca for modificado e nunca copiado, essa versão será muito boa. Não há condição de corrida porque o arquivo de script já precisa ser aberto antes que a verificação possa ser alcançada.
Ainda pode haver falsos positivos se outro processo tiver o arquivo de script aberto, mas observe que, mesmo que esteja aberto para edição no Vim, o vim na verdade não mantém o arquivo de script aberto, portanto não resultará em falsos positivos.
Mas lembre-se, não use essa abordagem se o script puder ser editado ou copiado, pois você obterá falsos negativos, ou seja, várias instâncias sendo executadas ao mesmo tempo - portanto, o fato de a edição com o Vim não fornecer falso-positivos não deve importar para você. I mencionar isso, porém, porque abordagem # 3 não dar falsos positivos (ou seja, se recusa a iniciar) se você tiver o script aberto com Vim.
Então o que fazer então?
A resposta mais votada para esta pergunta fornece uma boa abordagem sólida.
Talvez você possa escrever uma melhor ... mas se você não entender todos os problemas e advertências com todas as abordagens acima, provavelmente não escreverá um método de bloqueio que evite todas elas.
kill
editado; e parece uma boa prática armazenar o próprio pid no arquivo de bloqueio, em vez de apenas tocá-lo.