Come valutare i codici di risposta http dallo script bash / shell?


203

Ho la sensazione che mi manchi l'ovvio, ma non ci sono riuscito man [curl|wget]o google ("http" rende un termine di ricerca così brutto). Sto cercando una soluzione rapida e sporca a uno dei nostri server Web che spesso fallisce, restituendo il codice di stato 500 con un messaggio di errore. Una volta che ciò accade, deve essere riavviato.

Poiché la causa principale sembra essere difficile da trovare, puntiamo a una soluzione rapida, sperando che sia sufficiente a colmare il tempo fino a quando non saremo davvero in grado di risolverlo (il servizio non ha bisogno di alta disponibilità)

La soluzione proposta è quella di creare un processo cron che viene eseguito ogni 5 minuti, controllando http: // localhost: 8080 / . Se questo ritorna con il codice di stato 500, il server web verrà riavviato. Il server si riavvierà tra meno di un minuto, quindi non è necessario verificare la presenza di riavvii già in esecuzione.

Il server in questione è un'installazione minima di Ubuntu 8.04 con i pacchetti appena sufficienti installati per eseguire ciò di cui ha attualmente bisogno. Non è necessario eseguire l'attività in bash, ma mi piacerebbe che funzionasse in un ambiente così minimale senza installare altri interpreti.

(Conosco abbastanza bene gli script che il comando / opzioni per assegnare il codice di stato http a una variabile d'ambiente sarebbe sufficiente - questo è quello che ho cercato e che non sono riuscito a trovare.)

Risposte:


316

Non l'ho provato su un codice 500, ma funziona su altri come 200, 302 e 404.

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

Nota, il formato fornito per --write-out dovrebbe essere citato. Come suggerito da @ibai, aggiungi --headper fare una richiesta solo HEAD. Ciò consentirà di risparmiare tempo quando il recupero ha esito positivo poiché i contenuti della pagina non verranno trasmessi.


1
Bello - grazie: ho già trovato --write-out, ma ho perso --output / dev / null. Quando tutto il contenuto viene fornito con esso, il codice di risposta si perde in troppe informazioni, quindi semplicemente non l'ho visto ...
Olaf Kock

4
Posso memorizzare sia il codice di risposta che l'output in variabili separate? Vorrei fare eco all'output quando il codice di risposta non è 200
Vaibhav Bajpai

7
@VaibhavBajpai: prova questo: response=$(curl --write-out \\n%{http_code} --silent --output - servername)- l'ultima riga del risultato sarà il codice di risposta.
In pausa fino a nuovo avviso.

2
Questo non mostra lo stato della richiesta finale se il risultato della prima richiesta è un 3XX. Ad esempio, se il valore restituito è un reindirizzamento 301, questo script si ferma qui. Se aggiungi -IL, puoi ottenere lo stato finale. Se vuoi mostrare tutti gli stati HTTP per tutte le richieste, usa il mio esempio di seguito.
siliconrockstar,

Funziona benissimo, grazie! Tuttavia nel mio caso (https) ho dovuto mettere --insecureanche.
Tomasz Racia,

42
curl --write-out "%{http_code}\n" --silent --output /dev/null "$URL"

lavori. In caso contrario, devi premere Invio per visualizzare il codice stesso.


33

Avevo bisogno di provare qualcosa rapidamente oggi e ho pensato a questo. Ho pensato di inserirlo qui se qualcuno avesse bisogno di qualcosa di simile alla richiesta del 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" "my_email@email.com" -r "STATUS_CHECKER"
else
  exit 0
fi

Questo invierà un avviso e-mail su ogni cambio di stato da 200, quindi è stupido e potenzialmente avido. Per migliorare ciò, vorrei esaminare in modo circolare diversi codici di stato ed eseguire diverse azioni in base al risultato.


20

Sebbene la risposta accettata sia una buona risposta, trascura gli scenari di errore. curlritornerà 000se c'è un errore nella richiesta o c'è un errore di connessione.

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: questo va un po 'oltre il 500controllo dello stato richiesto per confermare anche che curlpuò persino connettersi al server (cioè restituisce 000).

Crea una funzione da essa:

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

Prova a ottenere un 500:

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

Test per ottenere errore / errore di connessione (ad es. 000):

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

Test che non ottiene un 500:

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

9

Con netcat e awk è possibile gestire manualmente la risposta del server:

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

9

Per seguire i reindirizzamenti 3XX e stampare i codici di risposta per tutte le richieste:

HTTP_STATUS="$(curl -IL --silent example.com | grep HTTP )";    
echo "${HTTP_STATUS}";

Il grepcatturerà tutte le linee con "http" in loro. Magari grep -m 1 HTTPafferrare solo la prima partita, se questo è l'intento, o forse invece reindirizzare su Awk per analizzare solo il codice del risultato.
Tripleee,

3

questo può aiutare a valutare lo stato http

var=`curl -I http://www.example.org 2>/dev/null | head -n 1 | awk -F" " '{print $2}'`
echo http:$var

2
head -n 1 | awk '{stuff}' è un po 'un antipasto, awk 'NR==1 {stuff}'fa la stessa cosa in un processo, Awk puro.
Tripleee,

3

Un'altra 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-)

2

Ecco lo script prolisso, ma facile da capire, ispirato alla soluzione di nicerobot , che richiede solo le intestazioni di risposta ed evita di usare IFS come suggerito qui . Emette un messaggio di rimbalzo quando incontra una risposta> = 400. Questa eco può essere sostituita con uno script di rimbalzo.

# 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"

1

non mi sono piaciute le risposte qui che mescolano i dati con lo stato. trovato questo: aggiungi il flag -f per far fallire il ricciolo e raccogli il codice di stato dell'errore dallo stato standard var: $?

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

non so se sia perfetto per ogni scenario qui, ma sembra adattarsi alle mie esigenze e penso che sia molto più facile lavorare con


1

Ecco la mia implementazione, che è un po 'più dettagliata di alcune delle risposte precedenti

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

0

Per aggiungere al commento @DennisWilliamson sopra:

@VaibhavBajpai: prova questo: response = $ (curl --write-out \ n% {http_code} --silent --output - servername) - l'ultima riga nel risultato sarà il codice di risposta

È quindi possibile analizzare il codice di risposta dalla risposta utilizzando qualcosa di simile al seguente, in cui X può indicare una regex per contrassegnare la fine della risposta (utilizzando un esempio json qui)

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

Vedi Rimozione sottostringa: http://tldp.org/LDP/abs/html/string-manipulation.html


Perché dovresti mettere il modello in una variabile e perché dovresti usare un inutileecho per ottenere il valore finale? Semplicemente code=${response##*\}}è più semplice ed evita una serie di insidie ​​comuni. Inoltre, questo è un modello glob, non una corretta espressione regolare.
Tripleee,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.