如何从bash CGI脚本解析$QUERY\u字符串?
我有一个正在CGI中使用的bash脚本。CGI通过读取URL中如何从bash CGI脚本解析$QUERY\u字符串?,bash,cgi,Bash,Cgi,我有一个正在CGI中使用的bash脚本。CGI通过读取URL中?后面的所有内容来设置$QUERY\u STRING环境变量。例如,设置QUERY\u STRING=a=123&b=456&c=ok 我在某处发现了以下丑陋之处: b=$(echo“$QUERY\u STRING”| sed-n's/^.*b=\([^&]*\).$/\1/p'| sed“s/%20//g”) 它将把$b设置为b的$QUERY\u字符串中的任何内容。但是,我的脚本已经增加到有十多个输入参数。是否有一种更简单的方法可以
?
后面的所有内容来设置$QUERY\u STRING
环境变量。例如,设置QUERY\u STRING=a=123&b=456&c=ok
我在某处发现了以下丑陋之处:
b=$(echo“$QUERY\u STRING”| sed-n's/^.*b=\([^&]*\).$/\1/p'| sed“s/%20//g”)
它将把$b设置为b
的$QUERY\u字符串中的任何内容。但是,我的脚本已经增加到有十多个输入参数。是否有一种更简单的方法可以自动将$QUERY\u字符串中的参数转换为bash可用的环境变量
也许我会使用某种形式的for循环,但如果脚本足够智能,能够自动检测每个参数,并可能构建一个如下所示的数组,那就更好了:
${parm[a]}=123
${parm[b]}=456
${parm[c]}=ok
我如何编写代码来实现这一点呢?您可以使用
IFS
来分解$QUERY
。例如,将其设置为&
$ 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
您可以在Bash4中保存到哈希/字典+
$ declare -A hash
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; hash[$1]=$2; done
$ echo ${hash["b"]}
456
试试这个:
saveIFS=$IFS
IFS='=&'
parm=($QUERY_STRING)
IFS=$saveIFS
现在你有了这个:
parm[0]=a
parm[1]=123
parm[2]=b
parm[3]=456
parm[4]=c
parm[5]=ok
在具有关联数组的Bash 4中,可以这样做(使用上面创建的数组):
编辑:
要在Bash 2及更高版本中使用间接寻址(使用上面创建的parm
数组):
您可以直接访问以下内容:
echo $var_a
或间接地:
echo $var_a
for p in a b c
do
name="var$p"
echo ${!name}
done
如果可能的话,最好使用它,因为它会使代码变得凌乱并成为bug的来源。处理CGI查询字符串的一个好方法是使用它作为Bash CGI脚本的包装器,并提供方便和安全的查询字符串解析。我将sed命令打包到另一个脚本中: $cat getvar.sh
s='s/^.*'${1}'=\([^&]*\).*$/\1/p'
echo $QUERY_STRING | sed -n $s | sed "s/%20/ /g"
我在我的主要cgi中将其称为:
id=`./getvar.sh id`
ds=`./getvar.sh ds`
dt=`./getvar.sh dt`
…等等,等等-你知道了。即使使用非常基本的busybox设备(本例中是我的PVR),我也能正常工作。根据正确的答案,我自己做了一些更改,以支持中的数组变量。我还添加了一个解码功能,我找不到作者给予一些信任 代码看起来有些凌乱,但它可以工作。如有更改和其他建议,将不胜感激
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
函数cgi_decodevar(){
[$#-ne 1]&返回
局部电压互感器
#将所有+替换为空白并追加%%
t=“${1/+/}%%”
而[${t}-gt 0-a“${t}”!=“%”,则
v=“${v}${t%%\%*}”消化到第一个%
t=“${t#*%}”#移除消化部分
#若有任何东西要解码,若并没有,则在字符串的末尾进行解码
如果[${t}-gt 0-a“${t}”!=“%”,那么
h=${t:0:2}#保存前两个字符
t=“${t:2}”#删除这些
v=“${v}”`echo-e\\\\x${h}`#将十六进制转换为特殊字符
fi
完成
#返回解码字符串
回显“${v}”
返回
}
saveIFS=$IFS
IFS='=&'
变量=($QUERY\u字符串)
IFS=$saveIFS
对于((i=0;i)来说,要使其更新,如果您有最新的Bash版本,则可以使用正则表达式实现这一点:
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
如果不想使用关联数组,只需更改倒数第二行即可。对于循环的每次迭代,参数位于${BASH\u REMATCH[1]}
中,其值位于${BASH\u REMATCH[2]}
中
这与短测试脚本中的函数相同,该脚本在数组上迭代输出查询字符串的参数及其值
#!/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
请注意,参数以相反的顺序在数组中结束(它是关联的,所以这不重要)。为什么不这样做
$ echo "${QUERY_STRING}"
name=carlo&last=lanza&city=pfungen-CH
$ saveIFS=$IFS
$ IFS='&'
$ eval $QUERY_STRING
$ IFS=$saveIFS
现在你有了这个
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
我只是简单地替换&to;。它将变成如下内容:
a=123;b=456;c=ok
因此,现在您只需评估和阅读您的VAR:
eval `echo "${QUERY_STRING}"|tr '&' ';'`
echo $a
echo $b
echo $c
请不要使用邪恶的评估垃圾 以下是如何可靠地解析字符串并获得关联数组:
declare -A param
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
param["$key"]=$value
done <<<"${QUERY_STRING}&"
要将查询字符串的内容转换为bash变量,请使用以下命令:
eval $(echo ${QUERY_STRING//&/;})
内部步骤echo${QUERY_STRING//&/;}
用分号替换所有的符号,生成a=123;b=456;c=ok,然后eval
将其计算到当前shell中
然后,可以将结果用作bash变量
echo $a
echo $b
echo $c
这些假设是:
- 值将永远不包含“&”
- 值永远不会包含“;”
- 查询字符串永远不会包含恶意代码
local re1='^(\w+=\w+)&?'
local re2='^(\w+)=(\w+)$'
local re1='^(\w+=(\w+|-|)+)&?'
local re2='^(\w+)=((\w+|-|)+)$'
这两条线:
local re1='^(\w+=\w+)&?'
local re2='^(\w+)=(\w+)$'
local re1='^(\w+=(\w+|-|)+)&?'
local re2='^(\w+)=((\w+|-|)+)$'
对于所有那些无法使用张贴的答案的人(比如我), 我明白了 不幸的是,他无法提升自己的职位 让我快速地将代码重新发布到这里:
!/bin/sh
如果[“$REQUEST_METHOD”=“POST”];则
如果[“$CONTENT_LENGTH”-gt 0];则
读取-n$CONTENT\u LENGTH POST\u DATA.bin
IFS='=&'
设置--$POST_数据
#2-价值1
#4-价值2
#6-价值3
#8-价值4
echo$2$4$6$8
echo“内容类型:文本/html”
回声“”
回显“已保存”
echo“收到的数据:$POST_数据”
echo“”
虽然公认的答案可能是最漂亮的,但在某些情况下,安全性非常重要,而且还需要从脚本中清晰可见
在这种情况下,首先我不会使用bash来完成任务,但是如果出于某种原因需要使用bash,那么最好避免使用这些新的数组字典特性,因为您无法确定它们到底是如何转义的
在这种情况下,好的原始解决方案可能会起作用:
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
这将迭代查询字符串的名称-值对
,没有办法用任何复杂的转义序列来绕过它,“
在bash中是一个非常强大的东西,除了由我们完全控制的单变量名称替换之外,没有什么可以欺骗的
此外,您可以将自己的处理代码注入“#…”
”中
local re1='^(\w+=\w+)&?'
local re2='^(\w+)=(\w+)$'
local re1='^(\w+=(\w+|-|)+)&?'
local re2='^(\w+)=((\w+|-|)+)$'
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
while IFS= read -r -d '&' KEYVAL && [[ -n "$KEYVAL" ]]; do
case ${KEYVAL%=*} in
key1) KEY1=${KEYVAL#*=} ;;
key2) KEY2=${KEYVAL#*=} ;;
esac
done <<END
$(echo "${QUERY_STRING}&")
END