Wie werden http-Antwortcodes aus dem Bash / Shell-Skript ausgewertet?

202

Ich habe das Gefühl, dass mir das Offensichtliche fehlt, aber es ist mir mit man [curl|wget]oder Google nicht gelungen ("http" macht einen so schlechten Suchbegriff). Ich suche nach einer schnellen und schmutzigen Lösung für einen unserer Webserver, die häufig ausfällt und den Statuscode 500 mit einer Fehlermeldung zurückgibt. Sobald dies geschieht, muss es neu gestartet werden.

Da die Hauptursache schwer zu finden zu sein scheint, streben wir eine schnelle Lösung an, in der Hoffnung, dass dies ausreicht, um die Zeit zu überbrücken, bis wir sie wirklich beheben können (der Service benötigt keine Hochverfügbarkeit).

Die vorgeschlagene Lösung besteht darin, einen Cron-Job zu erstellen, der alle 5 Minuten ausgeführt wird und http: // localhost: 8080 / überprüft . Wenn dies mit dem Statuscode 500 zurückgegeben wird, wird der Webserver neu gestartet. Der Server wird in weniger als einer Minute neu gestartet, sodass nicht nach bereits laufenden Neustarts gesucht werden muss.

Bei dem fraglichen Server handelt es sich um eine Ubuntu 8.04-Minimalinstallation mit gerade genug installierten Paketen, um das auszuführen, was derzeit benötigt wird. Es ist nicht unbedingt erforderlich, die Aufgabe in Bash auszuführen, aber ich möchte, dass sie in einer so minimalen Umgebung ausgeführt wird, ohne dass weitere Interpreter installiert werden.

(Ich bin mit Skripten hinreichend vertraut, sodass der Befehl / die Optionen zum Zuweisen des http-Statuscodes zu einer Umgebungsvariablen ausreichen würden - das habe ich gesucht und konnte es nicht finden.)

Olaf Kock
quelle

Antworten:

314

Ich habe dies nicht mit einem 500-Code getestet, aber es funktioniert mit anderen wie 200, 302 und 404.

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

Beachten Sie, dass das für --write-out bereitgestellte Format in Anführungszeichen gesetzt werden sollte. Fügen Sie, wie von @ibai vorgeschlagen, hinzu --head, um nur eine HEAD-Anfrage zu stellen. Dies spart Zeit, wenn der Abruf erfolgreich ist, da der Seiteninhalt nicht übertragen wird.

Bis auf weiteres angehalten.
quelle
1
Schön - danke: Ich habe --write-out bereits gefunden, aber --output / dev / null verpasst. Wenn der gesamte Inhalt damit verbunden ist, geht der Antwortcode in zu vielen Informationen verloren, sodass ich ihn einfach nicht gesehen habe ...
Olaf Kock
4
Kann ich sowohl den Antwortcode als auch die Ausgabe in separaten Variablen speichern? Ich möchte die Ausgabe
wiederholen,
7
@ VaibhavBajpai: Versuchen Sie Folgendes: response=$(curl --write-out \\n%{http_code} --silent --output - servername)- Die letzte Zeile im Ergebnis ist der Antwortcode.
Bis auf weiteres angehalten.
2
Dies zeigt nicht den endgültigen Anforderungsstatus an, wenn das Ergebnis der ersten Anforderung ein 3XX ist. Wenn der zurückgegebene Wert beispielsweise eine 301-Umleitung ist, stoppt dieses Skript nur dort. Wenn Sie -IL hinzufügen, können Sie den endgültigen Status erhalten. Wenn Sie alle HTTP-Status für alle Anforderungen anzeigen möchten, verwenden Sie mein Beispiel unten.
Siliconrockstar
Funktioniert super, danke! In meinem Fall (https) musste ich jedoch auch setzen --insecure.
Tomasz Racia
42
curl --write-out "%{http_code}\n" --silent --output /dev/null "$URL"

funktioniert. Wenn nicht, müssen Sie die Eingabetaste drücken, um den Code selbst anzuzeigen.

hd1
quelle
33

Ich musste heute schnell etwas vorführen und habe mir das ausgedacht. Ich dachte, ich würde es hier platzieren, wenn jemand etwas Ähnliches wie die Anfrage des OP benötigt.

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

Dies sendet eine E-Mail-Benachrichtigung bei jeder Statusänderung von 200, so dass es dumm und möglicherweise gierig ist. Um dies zu verbessern, würde ich versuchen, mehrere Statuscodes zu durchlaufen und je nach Ergebnis unterschiedliche Aktionen auszuführen.

Chris Gillatt
quelle
20

Obwohl die akzeptierte Antwort eine gute Antwort ist, werden Fehlerszenarien übersehen. curlwird zurückgegeben, 000wenn ein Fehler in der Anforderung vorliegt oder ein Verbindungsfehler vorliegt.

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

Hinweis: Dies geht ein wenig über die angeforderte 500Statusprüfung hinaus, um auch zu bestätigen, dass curlsogar eine Verbindung zum Server hergestellt werden kann (dh zurückgegeben wird)000 ).

Erstellen Sie daraus eine Funktion:

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

Testen Sie ein 500:

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

Testfehler / Verbindungsfehler (dh 000):

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

Test nicht bekommen 500:

failureCode http://httpbin.org/status/400 || echo not a failure
Nicerobot
quelle
9

Mit netcat und awk können Sie die Serverantwort manuell verarbeiten:

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
quelle
9

So folgen Sie 3XX-Weiterleitungen und drucken Antwortcodes für alle Anforderungen:

HTTP_STATUS="$(curl -IL --silent example.com | grep HTTP )";    
echo "${HTTP_STATUS}";
Siliconrockstar
quelle
Das greperfasst alle Zeilen mit "HTTP". Vielleicht, grep -m 1 HTTPum nur das erste Match zu holen, wenn das die Absicht ist, oder stattdessen zu Awk zu leiten, um nur den Ergebniscode zu analysieren.
Tripleee
3

Dies kann helfen, den http-Status zu bewerten

var=`curl -I http://www.example.org 2>/dev/null | head -n 1 | awk -F" " '{print $2}'`
echo http:$var
Tango
quelle
2
head -n 1 | awk '{stuff}' ist ein bisschen wie ein Antimuster, awk 'NR==1 {stuff}'macht dasselbe in einem Prozess, reines Awk.
Tripleee
3

Eine andere 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
quelle
2

Hier kommt das langwierige und dennoch leicht verständliche Skript, das von der Lösung von Nicerobot inspiriert ist und nur die Antwortheader anfordert und die Verwendung von IFS wie hier vorgeschlagen vermeidet . Es gibt eine Bounce-Nachricht aus, wenn eine Antwort> = 400 auftritt. Dieses Echo kann durch ein Bounce-Skript ersetzt werden.

# 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
quelle
1

Ich mochte die Antworten hier nicht, die die Daten mit dem Status mischen. Folgendes gefunden: Sie fügen das Flag -f hinzu, damit die Locke fehlschlägt, und übernehmen den Fehlerstatuscode aus dem Standardstatus var: $?

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

Ich weiß nicht, ob es für jedes Szenario hier perfekt ist, aber es scheint meinen Bedürfnissen zu entsprechen und ich denke, es ist viel einfacher, damit zu arbeiten

nathan g
quelle
1

Hier ist meine Implementierung, die etwas ausführlicher ist als einige der vorherigen Antworten

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
quelle
0

So fügen Sie @DennisWilliamson Kommentar oben hinzu:

@VaibhavBajpai: Versuchen Sie Folgendes: response = $ (curl --write-out \ n% {http_code} --silent --output - servername) - Die letzte Zeile im Ergebnis ist der Antwortcode

Sie können dann den Antwortcode aus der Antwort wie folgt analysieren, wobei X einen regulären Ausdruck kennzeichnen kann, um das Ende der Antwort zu markieren (anhand eines json-Beispiels hier).

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

Siehe Entfernen von Teilzeichenfolgen: http://tldp.org/LDP/abs/html/string-manipulation.html

user1015492
quelle
Warum würden Sie das Muster in eine Variable einfügen und warum würden Sie ein nutzloses verwendenecho , um den endgültigen Wert zu erhalten? Just code=${response##*\}}ist einfacher und vermeidet eine Reihe häufiger Fallstricke. Das ist auch ein Glob-Muster, kein richtiger regulärer Ausdruck.
Tripleee