Como avaliar os códigos de resposta http do script bash / shell?

203

Tenho a sensação de que estou perdendo o óbvio, mas não consegui man [curl|wget] google ("http" faz um termo de pesquisa tão ruim). Estou procurando uma correção rápida e suja para um de nossos servidores da Web que frequentemente falha, retornando o código de status 500 com uma mensagem de erro. Quando isso acontece, ele precisa ser reiniciado.

Como a causa raiz parece difícil de encontrar, estamos buscando uma solução rápida, esperando que seja suficiente para diminuir o tempo até que possamos corrigi-la (o serviço não precisa de alta disponibilidade)

A solução proposta é criar um trabalho cron que seja executado a cada 5 minutos, verificando http: // localhost: 8080 / . Se isso retornar com o código de status 500, o servidor da web será reiniciado. O servidor será reiniciado em menos de um minuto, portanto, não há necessidade de verificar se há reinicializações em execução.

O servidor em questão é uma instalação mínima do ubuntu 8.04 com apenas pacotes suficientes instalados para executar o que ele precisa atualmente. Não há um requisito difícil de executar a tarefa no bash, mas eu gostaria que ela fosse executada em um ambiente tão mínimo sem instalar mais intérpretes.

(Estou suficientemente familiarizado com scripts que o comando / opções para atribuir o código de status http a uma variável de ambiente seria suficiente - é isso que eu procurei e não consegui encontrar.)

Olaf Kock
fonte

Respostas:

316

Não testei isso em um código 500, mas funciona em outros como 200, 302 e 404.

response=$(curl --write-out '%{http_code}' --silent --output /dev/null servername)

Observe que o formato fornecido para --write-out deve ser citado. Conforme sugerido por @ibai, adicione --headpara fazer uma solicitação somente HEAD. Isso economizará tempo quando a recuperação for bem-sucedida, pois o conteúdo da página não será transmitido.

Pausado até novo aviso.
fonte
1
Agradável - obrigado: eu já encontrei --write-out, mas perdi o --output / dev / null. Quando todo o conteúdo vem com ele, o código de resposta se perde no muita informação, então eu simplesmente não vê-lo ...
Olaf Kock
4
Posso armazenar o código de resposta e a saída em variáveis ​​separadas? Eu gostaria de fazer eco a saída quando o código de resposta não é 200
Vaibhav Bajpai
7
@VaibhavBajpai: Tente o seguinte: response=$(curl --write-out \\n%{http_code} --silent --output - servername)- a última linha no resultado será o código de resposta.
Pausado até novo aviso.
2
Isso não mostra o status da solicitação final se o resultado da primeira solicitação for um 3XX. Por exemplo, se o valor retornado for um redirecionamento 301, esse script simplesmente pára por aí. Se você adicionar -IL, poderá obter o status final. Se você deseja mostrar todos os status HTTP para todas as solicitações, use meu exemplo abaixo.
Siliconrockstar
Trabalhando muito bem, obrigado! No entanto, no meu caso (https) eu precisava colocar --insecuretambém.
Tomasz Racia 12/09/16
42
curl --write-out "%{http_code}\n" --silent --output /dev/null "$URL"

trabalho. Caso contrário, você deve pressionar return para visualizar o próprio código.

hd1
fonte
33

Eu precisava demonstrar algo rapidamente hoje e veio com isso. Pensei em colocá-lo aqui se alguém precisasse de algo semelhante à solicitação do OP.

#!/bin/bash

status_code=$(curl --write-out %{http_code} --silent --output /dev/null www.bbc.co.uk/news)

if [[ "$status_code" -ne 200 ]] ; then
  echo "Site status changed to $status_code" | mail -s "SITE STATUS CHECKER" "[email protected]" -r "STATUS_CHECKER"
else
  exit 0
fi

Isso enviará um alerta por e-mail sobre cada alteração de estado de 200, por isso é idiota e potencialmente ganancioso. Para melhorar isso, eu examinaria vários códigos de status e executaria ações diferentes, dependendo do resultado.

Chris Gillatt
fonte
20

Embora a resposta aceita seja uma boa resposta, ela ignora os cenários de falha. curlretornará 000se houver um erro na solicitação ou se houver uma falha na conexão.

url='http://localhost:8080/'
status=$(curl --head --location --connect-timeout 5 --write-out %{http_code} --silent --output /dev/null ${url})
[[ $status == 500 ]] || [[ $status == 000 ]] && echo restarting ${url} # do start/restart logic

Nota: isso vai um pouco além da 500verificação de status solicitada para confirmar também que curlpode até se conectar ao servidor (ou seja, retorna 000).

Crie uma função a partir dela:

failureCode() {
    local url=${1:-http://localhost:8080}
    local code=${2:-500}
    local status=$(curl --head --location --connect-timeout 5 --write-out %{http_code} --silent --output /dev/null ${url})
    [[ $status == ${code} ]] || [[ $status == 000 ]]
}

Teste para obter um 500:

failureCode http://httpbin.org/status/500 && echo need to restart

Teste para obter erro / falha na conexão (ou seja 000):

failureCode http://localhost:77777 && echo need to start

Teste não obtendo um 500:

failureCode http://httpbin.org/status/400 || echo not a failure
nicerobot
fonte
9

Com o netcat e o awk, você pode manipular a resposta do servidor manualmente:

if netcat 127.0.0.1 8080 <<EOF | awk 'NR==1{if ($2 == "500") exit 0; exit 1;}'; then
GET / HTTP/1.1
Host: www.example.com

EOF

    apache2ctl restart;
fi
marco
fonte
9

Para seguir os redirecionamentos 3XX e imprimir códigos de resposta para todas as solicitações:

HTTP_STATUS="$(curl -IL --silent example.com | grep HTTP )";    
echo "${HTTP_STATUS}";
siliconrockstar
fonte
O grepirá capturar todas as linhas com "HTTP" nelas. Talvez grep -m 1 HTTPapenas pegue a primeira partida, se essa for a intenção, ou talvez vá até o Awk para analisar apenas o código do resultado.
tripleee
3

isso pode ajudar a avaliar o status http

var=`curl -I http://www.example.org 2>/dev/null | head -n 1 | awk -F" " '{print $2}'`
echo http:$var
Tango
fonte
2
head -n 1 | awk '{stuff}' é um pouco antipadrão, awk 'NR==1 {stuff}'faz a mesma coisa em um processo, o Awk puro.
tripleee
3

Outra variação:

       status=$(curl -sS  -I https://www.healthdata.gov/user/login  2> /dev/null | head -n 1 | cut -d' ' -f2)
status_w_desc=$(curl -sS  -I https://www.healthdata.gov/user/login  2> /dev/null | head -n 1 | cut -d' ' -f2-)
dkinzer
fonte
2

Aí vem o script longo, mas fácil de entender, inspirado na solução do nicerobot , que apenas solicita os cabeçalhos de resposta e evita o uso do IFS, conforme sugerido aqui . Ele gera uma mensagem de devolução quando encontra uma resposta> = 400. Esse eco pode ser substituído por um script de devolução.

# set the url to probe
url='http://localhost:8080'
# use curl to request headers (return sensitive default on timeout: "timeout 500"). Parse the result into an array (avoid settings IFS, instead use read)
read -ra result <<< $(curl -Is --connect-timeout 5 "${url}" || echo "timeout 500")
# status code is second element of array "result"
status=${result[1]}
# if status code is greater than or equal to 400, then output a bounce message (replace this with any bounce script you like)
[ $status -ge 400  ] && echo "bounce at $url with status $status"
Thomas Praxl
fonte
1

Não gostei das respostas aqui que misturam os dados com o status. encontrou o seguinte: você adiciona o sinalizador -f para que a ondulação falhe e escolhe o código de status do erro no status padrão var: $?

/unix/204762/return-code-for-curl-used-in-a-command-substitution

não sei se é perfeito para todos os cenários aqui, mas parece atender às minhas necessidades e acho muito mais fácil trabalhar com

nathan g
fonte
1

Aqui está minha implementação, que é um pouco mais detalhada do que algumas das respostas anteriores

curl https://somewhere.com/somepath   \
--silent \
--insecure \
--request POST \
--header "your-curl-may-want-a-header" \
--data @my.input.file \
--output site.output \
--write-out %{http_code} \
  > http.response.code 2> error.messages
errorLevel=$?
httpResponse=$(cat http.response.code)


jq --raw-output 'keys | @csv' site.output | sed 's/"//g' > return.keys
hasErrors=`grep --quiet --invert errors return.keys;echo $?`

if [[ $errorLevel -gt 0 ]] || [[ $hasErrors -gt 0 ]] || [[ "$httpResponse" != "200" ]]; then
  echo -e "Error POSTing https://somewhere.com/somepath with input my.input (errorLevel $errorLevel, http response code $httpResponse)" >> error.messages
  send_exit_message # external function to send error.messages to whoever.
fi
AG6HQ
fonte
0

Para adicionar ao @DennisWilliamson, o comentário acima:

@VaibhavBajpai: Experimente o seguinte: response = $ (curl --write-out \ n% {http_code} --silent --output - servername) - a última linha do resultado será o código de resposta

Em seguida, você pode analisar o código de resposta da resposta usando algo como o seguinte, em que X pode significar uma regex para marcar o final da resposta (usando um exemplo json aqui)

X='*\}'
code=$(echo ${response##$X})

Consulte Remoção de Substring: http://tldp.org/LDP/abs/html/string-manipulation.html

user1015492
fonte
Por que você colocaria o padrão em uma variável e por que usaria um inútilecho para obter o valor final? Just code=${response##*\}}é mais simples e evita uma série de armadilhas comuns. Além disso, esse é um padrão global, não uma expressão regular adequada.
tripleee