Por que obtenho status de saída diferente para ps | grep em um script?

11

Estou executando o script abaixo:

#!/bin/bash

ps ax  | grep -q [v]arnish
if [ $? -eq 0 ];then
        echo varnish is running...
        exit 0
else
        echo "Critical : varnish is not running "
        exit 2
fi

A saída é como:

[root@server ~]# sh -x check_varnish_pro.sh
+ ps ax
+ grep -q '[v]arnish'
+ '[' 0 -eq 0 ']'
+ echo varnish is running...
varnish is running...
+ exit 0

Quando executo o mesmo na linha de comando, estou obtendo o status de saída como 1:

[root@server ~]# ps ax  | grep -q [v]arnish; echo $?
1

O caso é como verniz não está instalado no servidor. Este script funciona bem em um servidor em que o verniz está instalado.

Por que status de saída diferente quando executado usando script e linha de comando? Como melhorar esse script?

prado
fonte
Use um sistema de supervisão de processo real, não esse tipo de invasão. Seu sistema operacional quase certamente terá uma maneira integrada de garantir que seus daemons que você deseja manter sejam reiniciados automaticamente em caso de falha, seja ele inicial, daemontools, systemd, launchd ou uma das muitas outras alternativas. Todos eles serão mais robustos e capazes do que esse tipo de invasão manual.
Charles Duffy

Respostas:

10

Quando você executa um script chamado check_varnish_pro.shteste

ps ax  | grep -q [v]arnish

é bem-sucedido porque existe um script chamado check_verniz em_pro execução.

AlexP
fonte
14

Em geral, é uma má idéia tentar a abordagem simples pse grepdeterminar se um determinado processo está em execução.

Você ficaria muito melhor usando pgrepisso:

if pgrep "varnish" >/dev/null; then
  echo "Varnish in running"
else
  echo "Varnish is not running"
fi

Veja o manual para pgrep. Em alguns sistemas (provavelmente não no Linux), você recebe um -qsinalizador que corresponde ao mesmo sinalizador para o grepqual se livra da necessidade de redirecionar /dev/null. Há também um -fsinalizador que executa a correspondência na linha de comando completa, e não apenas no nome do processo. Pode-se também limitar a correspondência aos processos pertencentes a um usuário específico usando -u.

A instalação pgreptambém fornece acesso ao pkillque permite sinalizar processos com base em seus nomes.

Além disso, se este é um daemon de serviço , e se o seu sistema Unix tem uma maneira de consultá-lo para obter informações (por exemplo, se ele está funcionando ou não), essa é a maneira correta de verificar isso.

No Linux, você possui systemctl( systemctl is-active --quiet varnishretornará 0 se estiver em execução, 3 caso contrário), no OpenBSD rcctl, etc.


Agora para o seu script:

No seu script, você analisa a saída de ps ax. Esta saída conterá o nome do próprio script check_varnish_pro.sh, que obviamente contém a string varnish. Isso lhe dá um falso positivo. Você teria percebido isso se o tivesse executado sem o -qsinalizador grepdurante o teste.

#!/bin/bash
ps ax | grep '[v]arnish'

Executando:

$ ./check_varnish_pro.sh
31004 p1  SN+     0:00.04 /bin/bash ./check_varnish_pro.sh

Outro problema é que, embora você tente "ocultar" o grepprocesso de ser detectado por grepsi mesmo usando [v]o padrão. Essa abordagem falhará se você executar o script ou a linha de comando em um diretório que contenha um arquivo ou diretório varnish(nesse caso, você receberá um falso positivo novamente). Isso ocorre porque o padrão não está entre aspas e o shell executará o nome do arquivo globbing com ele.

Vejo:

bash-4.4$ set -x
bash-4.4$ ps ax | grep [v]arnish
+ ps ax
+ grep '[v]arnish'
bash-4.4$ touch varnish
+ touch varnish
bash-4.4$ ps ax | grep [v]arnish
+ ps ax
+ grep varnish
91829 p2  SN+p    0:00.02 grep varnish

A presença do arquivo varnishfará com que o shell seja substituído [v]arnishpelo nome do arquivo varnishe você receberá um hit no padrão na tabela de processos (o grepprocesso).

Kusalananda
fonte
4
porque tudo é um arquivo "em linux land".
zee
@ z_ - Não tenho muita certeza de como está conectado, mas isso é válido mesmo nos Unices que não são Linux.
Kusalananda
4
Não apenas o processo grep; O nome do script check_varnish_pro.shtambém é um fator.
TNW 14/01
@ TNW Eu não vi isso no começo, mas você está certo. Vou acrescentar isso.
Kusalananda
3

O @AlexP explica muito sucintamente o que realmente está acontecendo, mas a idéia de @ Kusalananda de usar pgrep/ pkillpara um processo crítico é fortemente desencorajada . Melhores soluções incluem:

  • Perguntando ao serviço se está em execução. systemctl status varnishddeve cuidar disso em uma instalação moderna * nix.
  • Se, por alguma circunstância infeliz, você não tiver um serviço disponível, basta alterar o script de inicialização para relatar o problema assim que o processo terminar:

    varnish || true
    some_command_to_send_an_alert_that_the_service_has_died
  • Como alternativa, altere o script que inicia o serviço para registrar o PID e verifique o estado periodicamente com kill -0 "$pid".
l0b0
fonte
Eu concordo, eu estava apenas abordando os aspectos de script do shell do problema. Note que systemctlquase só está disponível no Linux (AFAIK), e não em todos os sistemas modernos do tipo Unix.
Kusalananda
A pergunta original tinha a tag "linux"; Não sei por que isso foi removido pelo @muru.
L0b0
Obrigado l0b0. Eu tinha duas perguntas "Por que" e "Como melhorar". A resposta de @ AlexP resolveu minha primeira pergunta e sua resposta é a melhor solução para a segunda pergunta. Mas Kusalananda explica coisas relacionadas a isso que acho que serão úteis para pessoas que têm problemas semelhantes. Então, agora estou confuso sobre o que aceitar como resposta.
Prado