bash
complet la solution. Démontrer comment analyser facilement d'autres en-têtes sans nécessiter awk
:
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
Production :
302
GFE/2.0
text/html; charset=UTF-8
Selon la RFC-2616, les en-têtes HTTP sont modélisés comme décrit dans "Standard for the Format of ARPA Internet Text Messages" (RFC822), qui indique clairement la section 3.1.2 :
Le nom du champ doit être composé de caractères ASCII imprimables (c'est-à-dire des caractères dont les valeurs sont comprises entre 33 et 126, décimal, sauf deux-points). Le corps du champ peut être composé de n'importe quel caractère ASCII, sauf CR ou LF. (Bien que CR et/ou LF puissent être présents dans le texte réel, ils sont supprimés par l'action de déplier le champ.)
Ainsi, le script ci-dessus devrait intercepter tout en-tête conforme à la RFC-[2]822 à l'exception notable des en-têtes repliés .
Si vous vouliez extraire plus de quelques en-têtes, vous pouvez placer tous les en-têtes dans un tableau associatif bash. Voici une fonction simple qui suppose qu'un en-tête donné n'apparaît qu'une seule fois. (Ne l'utilisez pas pour Set-Cookie
; voir ci-dessous.)
# 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")
}
Exemple :
$ 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
Notez que la réponse SO inclut un ou plusieurs cookies, en Set-Cookie
headers, mais nous ne pouvons voir que le dernier car le script naïf écrase les entrées avec le même nom d'en-tête. (Il se trouve qu'il n'y en avait qu'un mais nous ne pouvons pas le savoir.) Bien qu'il soit possible d'augmenter le script au cas particulier Set-Cookie
, une meilleure approche serait probablement de fournir un fichier cookie-jar et d'utiliser le -b
et -c
curl options afin de le maintenir.