J'ai un script bash qui est utilisé dans un CGI. Le CGI définit la variable d'environnement $ QUERY_STRING en lisant tout ce qui suit le ?
dans l'URL. Par exemple, http://example.com?a=123&b=456&c=ok sets QUERY_STRING=a=123&b=456&c=ok
.
Quelque part j'ai trouvé la laideur suivante:
b=$(echo "$QUERY_STRING" | sed -n 's/^.*b=\([^&]*\).*$/\1/p' | sed "s/%20/ /g")
qui définira $ b sur tout ce qui a été trouvé dans $ QUERY_STRING pour b
. Cependant, mon script a grandi pour avoir plus de dix paramètres d'entrée. Existe-t-il un moyen plus simple de convertir automatiquement les paramètres de $ QUERY_STRING en variables d’environnement utilisables par bash?
J'utiliserai peut-être simplement une boucle for, mais ce serait encore mieux si le script était suffisamment intelligent pour détecter automatiquement chaque paramètre et créer un tableau ressemblant à ceci:
$ {parm [a]} = 123 $ {parm [b]} = 456 $ {parm [c]} = ok
Comment pourrais-je écrire du code pour faire ça?
Essaye ça:
saveIFS=$IFS
IFS='=&'
parm=($QUERY_STRING)
IFS=$saveIFS
Maintenant vous avez ceci:
parm[0]=a
parm[1]=123
parm[2]=b
parm[3]=456
parm[4]=c
parm[5]=ok
Dans Bash 4, qui a des tableaux associatifs, vous pouvez le faire (en utilisant le tableau créé ci-dessus):
declare -A array
for ((i=0; i<${#parm[@]}; i+=2))
do
array[${parm[i]}]=${parm[i+1]}
done
qui vous donnera ceci:
array[a]=123
array[b]=456
array[c]=ok
Modifier:
Pour utiliser l'indirection dans Bash 2 et versions ultérieures (à l'aide du tableau parm
créé ci-dessus):
for ((i=0; i<${#parm[@]}; i+=2))
do
declare var_${parm[i]}=${parm[i+1]}
done
Ensuite, vous aurez:
var_a=123
var_b=456
var_c=ok
Vous pouvez y accéder directement:
echo $var_a
ou indirectement:
for p in a b c
do
name="var$p"
echo ${!name}
done
Si possible, il vaut mieux éviter d'éviter l'indirection car cela peut rendre le code compliqué et être une source de bogues.
vous pouvez décomposer $QUERY
avec IFS
. Par exemple, définissez-le sur &
$ QUERY="a=123&b=456&c=ok"
$ echo $QUERY
a=123&b=456&c=ok
$ IFS="&"
$ set -- $QUERY
$ echo $1
a=123
$ echo $2
b=456
$ echo $3
c=ok
$ array=($@)
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; echo $1 $2; done
a 123
b 456
c ok
Et vous pouvez enregistrer dans un hash/dictionnaire dans Bash 4+
$ declare -A hash
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; hash[$1]=$2; done
$ echo ${hash["b"]}
456
Pour convertir le contenu de QUERY_STRING en variables bash, utilisez la commande suivante:
eval $(echo ${QUERY_STRING//&/;})
L'étape interne, echo ${QUERY_STRING//&/;}
, remplace toutes les esperluettes par des points-virgules générant a = 123; b = 456; c = ok, que la variable eval
évalue dans le shell actuel.
Le résultat peut ensuite être utilisé comme variable bash.
echo $a
echo $b
echo $c
Les hypothèses sont les suivantes:
S'il vous plaît ne pas utiliser le mal eval maléfique.
Voici comment analyser de manière fiable la chaîne et obtenir un tableau associatif:
declare -A param
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
param["$key"]=$value
done <<<"${QUERY_STRING}&"
Si vous n'aimez pas la vérification des clés, vous pouvez le faire à la place:
declare -A param
while IFS='=' read -r -d '&' key value; do
param["$key"]=$value
done <<<"${QUERY_STRING:+"${QUERY_STRING}&"}"
Liste de toutes les clés et valeurs du tableau:
for key in "${!param[@]}"; do
echo "$key: ${param[$key]}"
done
J'ai intégré la commande sed dans un autre script:
$ cat getvar.sh
s='s/^.*'${1}'=\([^&]*\).*$/\1/p'
echo $QUERY_STRING | sed -n $s | sed "s/%20/ /g"
et je l'appelle depuis mon cgi principal en tant que:
id=`./getvar.sh id`
ds=`./getvar.sh ds`
dt=`./getvar.sh dt`
... etc, etc - vous avez une idée.
fonctionne pour moi même avec un appareil busybox très basique (mon PVR dans ce cas).
Je voudrais simplement remplacer le & to; Cela deviendra quelque chose comme:
a=123;b=456;c=ok
Alors maintenant, il vous suffit d’évaluer et de lire vos vars:
eval `echo "${QUERY_STRING}"|tr '&' ';'`
echo $a
echo $b
echo $c
Une bonne façon de gérer les chaînes de requête CGI consiste à utiliser Haserl , qui encapsule votre script Bash cgi et offre une analyse pratique et sécurisée des chaînes de requête.
@giacecco
Pour inclure un trait d'union dans l'expression régulière, vous pouvez modifier les deux lignes comme telles dans la réponse de @starfry.
Changer ces deux lignes:
local re1='^(\w+=\w+)&?'
local re2='^(\w+)=(\w+)$'
À ces deux lignes:
local re1='^(\w+=(\w+|-|)+)&?'
local re2='^(\w+)=((\w+|-|)+)$'
pourquoi pas ça
$ echo "${QUERY_STRING}"
name=carlo&last=lanza&city=pfungen-CH
$ saveIFS=$IFS
$ IFS='&'
$ eval $QUERY_STRING
$ IFS=$saveIFS
maintenant vous avez ceci
name = carlo
last = lanza
city = pfungen-CH
$ echo "name is ${name}"
name is carlo
$ echo "last is ${last}"
last is lanza
$ echo "city is ${city}"
city is pfungen-CH
Après la bonne réponse, je me suis moi-même apporté quelques modifications pour prendre en charge les variables de tableau, comme dans cette autre question . J'ai ajouté aussi une fonction de décodage dont je ne trouve pas l'auteur pour donner du crédit.
Le code semble un peu brouillon, mais cela fonctionne. Des changements et autres recommandations seraient grandement appréciés.
function cgi_decodevar() {
[ $# -ne 1 ] && return
local v t h
# replace all + with whitespace and append %%
t="${1//+/ }%%"
while [ ${#t} -gt 0 -a "${t}" != "%" ]; do
v="${v}${t%%\%*}" # digest up to the first %
t="${t#*%}" # remove digested part
# decode if there is anything to decode and if not at end of string
if [ ${#t} -gt 0 -a "${t}" != "%" ]; then
h=${t:0:2} # save first two chars
t="${t:2}" # remove these
v="${v}"`echo -e \\\\x${h}` # convert hex to special char
fi
done
# return decoded string
echo "${v}"
return
}
saveIFS=$IFS
IFS='=&'
VARS=($QUERY_STRING)
IFS=$saveIFS
for ((i=0; i<${#VARS[@]}; i+=2))
do
curr="$(cgi_decodevar ${VARS[i]})"
next="$(cgi_decodevar ${VARS[i+2]})"
prev="$(cgi_decodevar ${VARS[i-2]})"
value="$(cgi_decodevar ${VARS[i+1]})"
array=${curr%"[]"}
if [ "$curr" == "$next" ] && [ "$curr" != "$prev" ] ;then
j=0
declare var_${array}[$j]="$value"
Elif [ $i -gt 1 ] && [ "$curr" == "$prev" ]; then
j=$((j + 1))
declare var_${array}[$j]="$value"
else
declare var_$curr="$value"
fi
done
Pour tous ceux qui ne pouvaient pas le faire fonctionner avec les réponses affichées (comme moi), ce gars a compris.
Je ne peux malheureusement pas voter son message ...
Laissez-moi republier le code ici très rapidement:
#!/bin/sh
if [ "$REQUEST_METHOD" = "POST" ]; then
if [ "$CONTENT_LENGTH" -gt 0 ]; then
read -n $CONTENT_LENGTH POST_DATA <&0
fi
fi
#echo "$POST_DATA" > data.bin
IFS='=&'
set -- $POST_DATA
#2- Value1
#4- Value2
#6- Value3
#8- Value4
echo $2 $4 $6 $8
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Saved</title></head><body>"
echo "Data received: $POST_DATA"
echo "</body></html>"
J'espère que cela peut aider n'importe qui.
À votre santé
Pour mettre à jour cela, si vous avez une version récente de Bash, vous pouvez y parvenir avec des expressions régulières:
q="$QUERY_STRING"
re1='^(\w+=\w+)&?'
re2='^(\w+)=(\w+)$'
declare -A params
while [[ $q =~ $re1 ]]; do
q=${q##*${BASH_REMATCH[0]}}
[[ ${BASH_REMATCH[1]} =~ $re2 ]] && params+=([${BASH_REMATCH[1]}]=${BASH_REMATCH[2]})
done
Si vous ne souhaitez pas utiliser de tableaux associatifs, modifiez simplement l'avant-dernière ligne pour faire ce que vous voulez. Pour chaque itération de la boucle, le paramètre est dans ${BASH_REMATCH[1]}
et sa valeur est dans ${BASH_REMATCH[2]}
.
Voici la même chose qu'une fonction dans un script de test court qui effectue une itération sur le tableau et génère les paramètres de la chaîne de requête et leurs valeurs.
#!/bin/bash
QUERY_STRING='foo=hello&bar=there&baz=freddy'
get_query_string() {
local q="$QUERY_STRING"
local re1='^(\w+=\w+)&?'
local re2='^(\w+)=(\w+)$'
while [[ $q =~ $re1 ]]; do
q=${q##*${BASH_REMATCH[0]}}
[[ ${BASH_REMATCH[1]} =~ $re2 ]] && eval "$1+=([${BASH_REMATCH[1]}]=${BASH_REMATCH[2]})"
done
}
declare -A params
get_query_string params
for k in "${!params[@]}"
do
v="${params[$k]}"
echo "$k : $v"
done
Notez que les paramètres se retrouvent dans le tableau dans l'ordre inverse (c'est associatif, cela ne devrait donc pas avoir d'importance).
Bien que la réponse acceptée soit probablement la plus belle, il peut arriver que la sécurité soit extrêmement importante et qu'elle soit également bien visible depuis votre script.
Dans un tel cas, d’abord, je n’utiliserais pas bash pour la tâche, mais si cela devait être fait pour une raison quelconque, il serait peut-être préférable d’éviter ces nouvelles fonctions tableau - dictionnaire, car vous ne pouvez pas être sûr de leur exactitude. Ils se sont échappés.
Dans ce cas, les bonnes vieilles solutions primitives pourraient fonctionner:
QS="${QUERY_STRING}"
while [ "${QS}" != "" ]
do
nameval="${QS%%&*}"
QS="${QS#$nameval}"
QS="${QS#&}"
name="${nameval%%=*}"
val="${nameval#$name}"
val="${nameval#=}"
# and here we have $name and $val as names and values
# ...
done
Ceci itère sur les paires nom-valeur du QUERY_STRING
, et il n'y a aucun moyen de le contourner avec une séquence d'échappement délicate - le "
est très puissant dans bash, à l'exception d'une seule substitution de nom de variable, que nous contrôlons entièrement, rien ne peut être trompé.
De plus, vous pouvez injecter votre propre code de traitement dans "# ...
". Cela vous permet de n'autoriser que votre propre liste bien définie (et, idéalement, courte) des noms de variables autorisés. Inutile de dire que LD_PRELOAD
ne devrait pas en faire partie. ;-)
De plus, aucune variable ne sera exportée, et exclusivement QS
, nameval
, name
et val
est utilisé.