Jak oceniać kody odpowiedzi http ze skryptu bash / shell?

203

Mam wrażenie, że brakuje mi tego, co oczywiste, ale nie udało mi się man [curl|wget]ani google („http” sprawia, że ​​wyszukiwane hasło jest złe). Szukam szybkiego i brudnego rozwiązania dla jednego z naszych serwerów, który często się nie udaje, zwracając kod stanu 500 z komunikatem o błędzie. Gdy to się stanie, należy go ponownie uruchomić.

Ponieważ podstawowa przyczyna wydaje się być trudna do znalezienia, dążymy do szybkiej naprawy, mając nadzieję, że wystarczy czasu, aby naprawdę ją naprawić (usługa nie wymaga wysokiej dostępności)

Proponowanym rozwiązaniem jest utworzenie zadania cron, które będzie uruchamiane co 5 minut, sprawdzając http: // localhost: 8080 / . Jeśli to zwróci kod stanu 500, serwer WWW zostanie zrestartowany. Serwer uruchomi się ponownie za niecałą minutę, więc nie ma potrzeby sprawdzania, czy restart już działa.

Serwer, o którym mowa, to minimalna instalacja Ubuntu 8.04 z zainstalowanymi wystarczającymi pakietami, aby uruchomić to, czego aktualnie potrzebuje. Nie ma twardego wymagania, aby wykonać zadanie w bash, ale chciałbym, aby działało w tak minimalnym środowisku bez instalowania żadnych interpreterów.

(Jestem wystarczająco zaznajomiony ze skryptami, że wystarczy polecenie / opcje przypisania kodu stanu HTTP do zmiennej środowiskowej - tego szukałem i nie mogłem znaleźć).

Olaf Kock
źródło

Odpowiedzi:

316

Nie testowałem tego na kodzie 500, ale działa na innych, takich jak 200, 302 i 404.

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

Uwaga: należy podać format przewidziany dla opcji --write-out. Jak sugeruje @ibai, dodaj, --headaby złożyć żądanie HEAD. Zaoszczędzi to czas, gdy pobieranie się powiedzie, ponieważ zawartość strony nie zostanie przesłana.

Wstrzymano do odwołania.
źródło
1
Fajnie - dzięki: już znalazłem --write-out, ale przegapiłem --output / dev / null. Gdy cała treść się z tym wiąże, kod odpowiedzi gubi się w zbyt dużej ilości informacji, więc po prostu jej nie widziałem ...
Olaf Kock
4
Czy mogę przechowywać kod odpowiedzi i dane wyjściowe w osobnych zmiennych? Chciałbym powtórzyć dane wyjściowe, gdy kod odpowiedzi nie ma wartości 200
Vaibhav Bajpai
7
@VaibhavBajpai: Spróbuj tego: response=$(curl --write-out \\n%{http_code} --silent --output - servername)- ostatnim wierszem w wyniku będzie kod odpowiedzi.
Wstrzymano do odwołania.
2
Nie pokazuje statusu końcowego żądania, jeśli wynikiem pierwszego żądania jest 3XX. Na przykład, jeśli zwrócona wartość to przekierowanie 301, skrypt ten po prostu się tam zatrzymuje. Jeśli dodasz -IL, możesz uzyskać ostateczny status. Jeśli chcesz pokazać wszystkie stany HTTP dla wszystkich żądań, skorzystaj z mojego przykładu poniżej.
siliconrockstar
Działa świetnie, dzięki! Jednak w moim przypadku (https) musiałem również umieścić --insecure.
Tomasz Racia
42
curl --write-out "%{http_code}\n" --silent --output /dev/null "$URL"

Pracuje. Jeśli nie, musisz nacisnąć Return, aby wyświetlić sam kod.

hd1
źródło
33

Musiałem dziś szybko coś pokazać i wymyśliłem to. Pomyślałem, że umieściłbym to tutaj, gdyby ktoś potrzebował czegoś podobnego do wniosku 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

Spowoduje to wysłanie powiadomienia e-mail o każdej zmianie stanu z 200, więc jest głupi i potencjalnie chciwy. Aby to poprawić, przyjrzałbym się zapętlaniu kilku kodów statusu i wykonywaniu różnych akcji zależnych od wyniku.

Chris Gillatt
źródło
20

Chociaż zaakceptowana odpowiedź jest dobrą odpowiedzią, nie uwzględnia scenariuszy awarii. curlzwróci, 000jeśli wystąpi błąd w żądaniu lub wystąpi błąd połączenia.

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

Uwaga: wykracza to nieco poza wymaganą 500kontrolę stanu, aby potwierdzić, że curlmożna nawet połączyć się z serwerem (tzn. Zwraca 000).

Utwórz z niego funkcję:

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

Przetestuj uzyskanie 500:

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

Sprawdź, czy wystąpił błąd / awaria połączenia (tj. 000):

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

Test nie otrzymuje 500:

failureCode http://httpbin.org/status/400 || echo not a failure
nicerobot
źródło
9

Za pomocą netcat i awk możesz ręcznie obsłużyć odpowiedź serwera:

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
źródło
9

Aby śledzić przekierowania 3XX i drukować kody odpowiedzi dla wszystkich żądań:

HTTP_STATUS="$(curl -IL --silent example.com | grep HTTP )";    
echo "${HTTP_STATUS}";
siliconrockstar
źródło
grepPrzechwyci wszystkie linie z „http” w nich. Być może, grep -m 1 HTTPaby złapać tylko pierwsze dopasowanie, jeśli taka jest intencja, lub może zamiast tego przesłać potokiem do Awk, aby przeanalizować tylko kod wyniku.
tripleee
3

może to pomóc w ocenie stanu HTTP

var=`curl -I http://www.example.org 2>/dev/null | head -n 1 | awk -F" " '{print $2}'`
echo http:$var
Tango
źródło
2
head -n 1 | awk '{stuff}' jest trochę antypatternem, awk 'NR==1 {stuff}'robi to samo w jednym procesie, czysty Awk.
tripleee
3

Kolejna odmiana:

       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
źródło
2

Nadchodzi od dawna - ale łatwy do zrozumienia - skrypt, zainspirowany rozwiązaniem nicerobot , który żąda tylko nagłówków odpowiedzi i unika używania IFS zgodnie z sugestią tutaj . Wysyła komunikat odbicia, gdy napotka odpowiedź> = 400. To echo można zastąpić skryptem odbicia.

# 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
źródło
1

Nie podobały mi się tutaj odpowiedzi łączące dane ze statusem. znaleziono to: dodajesz flagę -f, aby curl się nie powiódł i odbierasz kod statusu błędu ze standardowego statusu var: $?

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

nie wiem, czy jest idealny do każdego scenariusza tutaj, ale wydaje się, że pasuje do moich potrzeb i myślę, że o wiele łatwiej jest z nim pracować

nathan g
źródło
1

Oto moja implementacja, która jest nieco bardziej szczegółowa niż niektóre poprzednie odpowiedzi

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
OS6HQ
źródło
0

Aby dodać do komentarza @DennisWilliamson powyżej:

@VaibhavBajpai: Spróbuj tego: response = $ (curl --write-out \ n% {http_code} --silent --output - nazwa serwera) - ostatnim wierszem w wyniku będzie kod odpowiedzi

Następnie możesz przeanalizować kod odpowiedzi z odpowiedzi, używając czegoś takiego, jak poniżej, gdzie X może oznaczać regex w celu oznaczenia końca odpowiedzi (używając tutaj przykładu json)

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

Zobacz Usuwanie podciągów: http://tldp.org/LDP/abs/html/string-manipulation.html

użytkownik1015492
źródło
Dlaczego wstawiałbyś wzorzec do zmiennej i dlaczego miałbyś używać bezużytecznegoecho do uzyskania ostatecznej wartości? Po prostu code=${response##*\}}jest prostszy i pozwala uniknąć wielu typowych pułapek. Jest to również wzorzec globalny, a nie właściwe wyrażenie regularne.
tripleee