Como posso usar o bash se testar e encontrar comandos juntos?

22

Eu tenho um diretório com logs de travamento e gostaria de usar uma instrução condicional em um script bash baseado em um comando find.

Os arquivos de log são armazenados neste formato:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

Eu quero que a instrução if retorne true somente se houver um log de falhas para um aplicativo específico que foi modificado nos últimos 5 minutos. O comando find que eu usaria é:

find /var/log/crashes -name app-\*\.log -mmin -5

Não sei como incorporar isso em uma instrução if corretamente. Eu acho que isso pode funcionar:

if [ test 'find /var/log/crashes -name app-\*\.log -mmin -5' ] then
 service myapp restart
fi

Existem algumas áreas em que não estou claro:

  • Eu olhei para os sinalizadores , mas não tenho certeza um, se houver, que eu deveria usar.
  • Preciso da diretiva test ou devo processar diretamente os resultados do comando find ou usar find... | wc -l para obter uma contagem de linhas?
  • Não é necessário 100% para responder a essa pergunta, mas test é para testar os códigos de retorno retornados pelos comandos? E eles são meio invisíveis - fora de stdout / stderr ? Eu li a página man , mas ainda não sei bem quando usar test e como depurá-la.
por cwd 28.08.2012 / 21:06

5 respostas

24

[ e test são sinônimos (exceto [ requer ] ), portanto, você não deseja usar [ test :

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

test retorna um status de saída zero se a condição for verdadeira, caso contrário, diferente de zero. Isso pode ser substituído por qualquer programa para verificar seu status de saída, onde 0 indica sucesso e diferente de zero indica falha:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

No entanto, todos os exemplos acima apenas testam o status de saída do programa e ignoram a saída do programa.

Para find , você precisará testar se alguma saída foi gerada. -n testa uma string não vazia:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

Uma lista completa de argumentos de teste está disponível chamando help test na linha de comando bash .

Se você estiver usando bash (e não sh ), poderá usar [[ condition ]] , que se comporta de maneira mais previsível quando há espaços ou outros casos especiais em sua condição. Caso contrário, geralmente é o mesmo que usar [ condition ] . Eu usei [[ condition ]] neste exemplo, como faço sempre que possível.

Eu também alterei 'command' para $(command) , que também geralmente se comporta de forma semelhante, mas é melhor com comandos aninhados.

    
por 28.08.2012 / 21:49
9

find sairá com êxito se não houver erros, portanto você não poderá contar com o status de saída para saber se encontrou algum arquivo. Mas, como você disse, você pode contar quantos arquivos encontrou e testar esse número.

Seria algo assim:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test (também conhecido como [ ) não verifica os códigos de erro dos comandos, tem uma sintaxe especial para fazer testes e sai com um código de erro 0 se o teste foi bem-sucedido ou 1 caso contrário . É if aquele que verifica o código de erro do comando que você passa para ele e executa seu corpo baseado nele.

Consulte man test (ou help test , se você usar bash ) e help if (idem).

Nesse caso, wc -l emitirá um número. Usamos a opção test de -gt para testar se esse número é maior que 0 . Se for, test (ou [ ) retornará com o código de saída 0 . if interpretará esse código de saída como um sucesso e executará o código dentro de seu corpo.

    
por 28.08.2012 / 21:46
1

Isso seria

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

ou

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

Os comandos test e [ … ] são exatamente sinônimos. A única diferença é o nome deles e o fato de que [ requer um ] de fechamento como seu último argumento. Como sempre, use aspas duplas em torno da substituição do comando, caso contrário, a saída do comando find será quebrada em palavras, e aqui você obterá um erro de sintaxe se houver mais de um arquivo correspondente (e quando não houver argumentos , [ -n ] é verdadeiro, enquanto você deseja [ -n "" ] , que é falso).

Em ksh, bash e zsh, mas não em cinza, você também pode usar [[ … ]] , que possui regras de análise diferentes: [ é um comando comum, enquanto [[ … ]] é uma construção de análise diferente. Você não precisa de aspas duplas dentro de [[ … ]] (embora elas não doem). Você ainda precisa do ; após o comando.

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

Isso pode ser potencialmente ineficiente: se houver muitos arquivos em /var/log/crashes , o find explorará todos eles. Você deve fazer encontrar parar assim que encontrar um jogo, ou logo depois. Com o GNU find (Linux não integrado, Cygwin), use o -quit primary.

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

Com outros sistemas, canalize find em head para pelo menos sair logo após a primeira partida (a localização irá morrer de um cano quebrado).

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(Você pode usar head -c 1 se o seu comando head for compatível.)

Como alternativa, use zsh.

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then
    
por 31.08.2012 / 03:36
1

Isso deve funcionar

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi
    
por 19.02.2013 / 04:26
0
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

é uma solução apropriada aqui.

-exec service myapp restart ';' faz com que find invoque o comando que você deseja executar diretamente, em vez de precisar que o shell interprete nada.

-quit faz com que find saia depois de processar o comando, evitando que o comando seja executado novamente se houver vários arquivos que correspondam aos critérios.

    
por 01.02.2018 / 19:16