Vollständiger bash
Lösung. Demonstrieren Sie, wie Sie andere Header einfach parsen können, ohne dass awk
erforderlich ist :
shopt -s extglob # Required to trim whitespace; see below
while IFS=':' read key value; do
# trim whitespace in "value"
value=${value##+([[:space:]])}; value=${value%%+([[:space:]])}
case "$key" in
Server) SERVER="$value"
;;
Content-Type) CT="$value"
;;
HTTP*) read PROTO STATUS MSG <<< "$key{$value:+:$value}"
;;
esac
done < <(curl -sI http://www.google.com)
echo $STATUS
echo $SERVER
echo $CT
Produzieren:
302
GFE/2.0
text/html; charset=UTF-8
Gemäß RFC-2616 werden HTTP-Header wie in "Standard for the Format of ARPA Internet Text Messages" beschrieben modelliert (RFC822), in dem Abschnitt 3.1.2 eindeutig angegeben ist:
Der Feldname muss aus druckbaren ASCII-Zeichen bestehen (d. h. Zeichen mit Werten zwischen 33. und 126. Dezimal, außer Doppelpunkt). Der Feldkörper kann aus beliebigen ASCII-Zeichen bestehen, außer CR oder LF. (Während CR und/oder LF im eigentlichen Text vorhanden sein können, werden sie durch die Aktion des Aufklappens des Felds entfernt.)
Also das obige Skript sollte jeden RFC-[2]822-konformen Header abfangen mit der bemerkenswerten Ausnahme gefalteter Header .
Wenn Sie mehr als ein paar Header extrahieren möchten, können Sie alle Header in ein assoziatives Bash-Array packen. Hier ist eine einfache Funktion, die davon ausgeht, dass ein bestimmter Header nur einmal vorkommt. (Verwenden Sie es nicht für Set-Cookie
; siehe unten.)
# Call this as: headers ARRAY URL
headers () {
{
# (Re)define the specified variable as an associative array.
unset $1;
declare -gA $1;
local line rest
# Get the first line, assuming HTTP/1.0 or above. Note that these fields
# have Capitalized names.
IFS=$' \t\n\r' read $1[Proto] $1[Status] rest
# Drop the CR from the message, if there was one.
declare -gA $1[Message]="${rest%$'\r'}"
# Now read the rest of the headers.
while true; do
# Get rid of the trailing CR if there is one.
IFS=$'\r' read line rest;
# Stop when we hit an empty line
if [[ -z $line ]]; then break; fi
# Make sure it looks like a header
# This regex also strips leading and trailing spaces from the value
if [[ $line =~ ^([[:alnum:]_-]+):\ *(( *[^ ]+)*)\ *$ ]]; then
# Force the header to lower case, since headers are case-insensitive,
# and store it into the array
declare -gA $1[${BASH_REMATCH[1],,}]="${BASH_REMATCH[2]}"
else
printf "Ignoring non-header line: %q\n" "$line" >> /dev/stderr
fi
done
} < <(curl -Is "$2")
}
Beispiel:
$ headers so http://stackoverflow.com/
$ for h in ${!so[@]}; do printf "%s=%s\n" $h "${so[$h]}"; done | sort
Message=OK
Proto=HTTP/1.1
Status=200
cache-control=public, no-cache="Set-Cookie", max-age=43
content-length=224904
content-type=text/html; charset=utf-8
date=Fri, 25 Jul 2014 17:35:16 GMT
expires=Fri, 25 Jul 2014 17:36:00 GMT
last-modified=Fri, 25 Jul 2014 17:35:00 GMT
set-cookie=prov=205fd7f3-10d4-4197-b03a-252b60df7653; domain=.stackoverflow.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly
vary=*
x-frame-options=SAMEORIGIN
Beachten Sie, dass die SO-Antwort in Set-Cookie
ein oder mehrere Cookies enthält Header, aber wir können nur den letzten sehen, weil das naive Skript Einträge mit demselben Header-Namen überschreibt. (Zufälligerweise gab es nur einen, aber das können wir nicht wissen.) Während es möglich wäre, das Skript auf den Sonderfall Set-Cookie
zu erweitern , wäre ein besserer Ansatz wahrscheinlich, eine Cookie-Jar-Datei bereitzustellen und den -b
zu verwenden und -c
curl-Optionen, um es zu pflegen.