Comment évaluer les codes de réponse http à partir du script bash / shell?

203

J'ai le sentiment que je manque l'évidence, mais je n'ai pas réussi avec man [curl|wget]ou google ("http" fait un si mauvais terme de recherche). Je cherche une solution rapide et sale à l'un de nos serveurs Web qui échoue fréquemment, renvoyant le code d'état 500 avec un message d'erreur. Une fois que cela se produit, il doit être redémarré.

Comme la cause première semble difficile à trouver, nous visons une solution rapide, en espérant qu'elle suffira à combler le temps jusqu'à ce que nous puissions vraiment la résoudre (le service n'a pas besoin de haute disponibilité)

La solution proposée consiste à créer un travail cron qui s'exécute toutes les 5 minutes, en vérifiant http: // localhost: 8080 / . Si cela revient avec le code d'état 500, le serveur Web sera redémarré. Le serveur redémarrera dans moins d'une minute, il n'est donc pas nécessaire de vérifier les redémarrages déjà en cours d'exécution.

Le serveur en question est une installation minimale ubuntu 8.04 avec juste assez de packages installés pour exécuter ce dont il a besoin actuellement. Il n'y a aucune exigence difficile pour effectuer la tâche dans bash, mais j'aimerais qu'elle s'exécute dans un environnement aussi minimal sans installer plus d'interprètes.

(Je suis suffisamment familier avec les scripts pour que la commande / options pour attribuer le code de statut http à une variable d'environnement soit suffisante - c'est ce que j'ai cherché et que je n'ai pas pu trouver.)

Olaf Kock
la source

Réponses:

316

Je n'ai pas testé cela sur un code 500, mais cela fonctionne sur d'autres comme 200, 302 et 404.

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

Remarque, le format fourni pour --write-out doit être cité. Comme suggéré par @ibai, ajoutez --headpour faire une demande HEAD uniquement. Cela permettra de gagner du temps lorsque la récupération est réussie car le contenu de la page ne sera pas transmis.

En pause jusqu'à nouvel ordre.
la source
1
Bien - merci: j'ai déjà trouvé --write-out, mais j'ai raté le --output / dev / null. Lorsque tout le contenu vient avec, le code de réponse se perd dans trop d'informations, donc je ne l'ai tout simplement pas vu ...
Olaf Kock
4
Puis-je stocker le code de réponse et la sortie dans des variables distinctes? Je voudrais faire écho à la sortie lorsque le code de réponse n'est pas 200
Vaibhav Bajpai
7
@VaibhavBajpai: Essayez ceci: response=$(curl --write-out \\n%{http_code} --silent --output - servername)- la dernière ligne du résultat sera le code de réponse.
pause jusqu'à nouvel ordre.
2
Cela ne montre pas l'état de la demande finale si le résultat de la première demande est un 3XX. Par exemple, si la valeur renvoyée est une redirection 301, ce script s'arrête simplement là. Si vous ajoutez -IL, vous pouvez obtenir le statut final. Si vous souhaitez afficher tous les statuts HTTP pour toutes les demandes, utilisez mon exemple ci-dessous.
siliconrockstar
Fonctionne très bien, merci! Cependant, dans mon cas (https), je devais également le mettre --insecure.
Tomasz Racia
42
curl --write-out "%{http_code}\n" --silent --output /dev/null "$URL"

travaux. Sinon, vous devez appuyer sur Retour pour afficher le code lui-même.

hd1
la source
33

J'avais besoin de faire une démonstration rapide de quelque chose aujourd'hui et j'ai trouvé ça. Je pensais que je le placerais ici si quelqu'un avait besoin de quelque chose de similaire à la demande du PO.

#!/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

Cela enverra une alerte par e-mail à chaque changement d'état de 200, donc c'est stupide et potentiellement gourmand. Pour améliorer cela, je chercherais à parcourir plusieurs codes d'état et à effectuer différentes actions en fonction du résultat.

Chris Gillatt
la source
20

Bien que la réponse acceptée soit une bonne réponse, elle ignore les scénarios d'échec. curlretournera 000s'il y a une erreur dans la demande ou en cas d'échec de connexion.

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

Remarque: cela va un peu au-delà de la 500vérification d'état demandée pour confirmer également que vous curlpouvez même vous connecter au serveur (c'est-à-dire les retours 000).

Créez-en une fonction:

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 ]]
}

Testez l'obtention d'un 500:

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

Tester l'erreur / l'échec de connexion (c.-à-d. 000):

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

Le test ne reçoit pas 500:

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

Avec netcat et awk, vous pouvez gérer la réponse du serveur manuellement:

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
la source
9

Pour suivre les redirections 3XX et imprimer les codes de réponse pour toutes les demandes:

HTTP_STATUS="$(curl -IL --silent example.com | grep HTTP )";    
echo "${HTTP_STATUS}";
siliconrockstar
la source
Le grepcapturera toutes les lignes contenant "HTTP". Peut-être grep -m 1 HTTPpour ne saisir que la première correspondance, si c'est l'intention, ou peut-être plutôt diriger vers Awk pour analyser uniquement le code de résultat.
tripleee
3

cela peut aider à évaluer le statut http

var=`curl -I http://www.example.org 2>/dev/null | head -n 1 | awk -F" " '{print $2}'`
echo http:$var
Tango
la source
2
head -n 1 | awk '{stuff}' est un peu un contre-modèle, awk 'NR==1 {stuff}'fait la même chose en un seul processus, Awk pur.
tripleee
3

Une autre variante:

       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
la source
2

Voici le script de longue haleine - mais facile à comprendre -, inspiré de la solution de nicerobot , qui ne demande que les en-têtes de réponse et évite d'utiliser IFS comme suggéré ici . Il émet un message de rebond lorsqu'il rencontre une réponse> = 400. Cet écho peut être remplacé par un script de rebond.

# 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
la source
1

je n'ai pas aimé les réponses ici qui mélangent les données avec le statut. trouvé ceci: vous ajoutez l'indicateur -f pour que curl échoue et récupérez le code d'état d'erreur dans le statut standard var: $?

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

Je ne sais pas si c'est parfait pour tous les scénarios ici, mais cela semble correspondre à mes besoins et je pense qu'il est beaucoup plus facile de travailler avec

nathan g
la source
1

Voici mon implémentation, qui est un peu plus détaillée que certaines des réponses précédentes

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
la source
0

Pour ajouter au commentaire @DennisWilliamson ci-dessus:

@VaibhavBajpai: Essayez ceci: response = $ (curl --write-out \ n% {http_code} --silent --output - servername) - la dernière ligne du résultat sera le code de réponse

Vous pouvez ensuite analyser le code de réponse de la réponse en utilisant quelque chose comme le suivant, où X peut signifier une expression régulière pour marquer la fin de la réponse (en utilisant un exemple json ici)

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

Voir Suppression de sous-chaîne: http://tldp.org/LDP/abs/html/string-manipulation.html

user1015492
la source
Pourquoi mettriez-vous le motif dans une variable, et pourquoi utiliseriez-vous un inutileecho pour obtenir la valeur finale? Tout code=${response##*\}}est plus simple et évite un certain nombre d’écueils courants. En outre, c'est un modèle global, pas une expression régulière appropriée.
tripleee