如何从 bash CGI 脚本解析 $QUERY_STRING?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3919755/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to parse $QUERY_STRING from a bash CGI script?
提问by User1
I have a bash script that is being used in a CGI. The CGI sets the $QUERY_STRING
environment variable by reading everything after the ?
in the URL. For example, http://example.com?a=123&b=456&c=oksets QUERY_STRING=a=123&b=456&c=ok
.
我有一个在 CGI 中使用的 bash 脚本。CGI$QUERY_STRING
通过读取?
URL之后的所有内容来设置环境变量。例如,http://example.com?a=123&b=456&c=ok集QUERY_STRING=a=123&b=456&c=ok
。
Somewhere I found the following ugliness:
在某处我发现了以下丑陋之处:
b=$(echo "$QUERY_STRING" | sed -n 's/^.*b=\([^&]*\).*$/\1/p' | sed "s/%20/ /g")
b=$(echo "$QUERY_STRING" | sed -n 's/^.*b=\([^&]*\).*$/\1/p' | sed "s/%20/ /g")
which will set $b to whatever was found in $QUERY_STRING for b
. However, my script has grown to have over ten input parameters. Is there an easier way to automatically convert the parameters in $QUERY_STRING into environment variables usable by bash?
这会将 $b 设置为在 $QUERY_STRING 中找到的任何内容b
。但是,我的脚本已经增长到有十多个输入参数。是否有更简单的方法将 $QUERY_STRING 中的参数自动转换为 bash 可用的环境变量?
Maybe I'll just use a for loop of some sort, but it'd be even better if the script was smart enough to automatically detect each parameter and maybe build an array that looks something like this:
也许我只会使用某种形式的 for 循环,但如果脚本足够智能以自动检测每个参数并可能构建一个如下所示的数组,那就更好了:
${parm[a]}=123
${parm[b]}=456
${parm[c]}=ok
How could I write code to do that?
我怎么能写代码来做到这一点?
回答by Paused until further notice.
Try this:
尝试这个:
saveIFS=$IFS
IFS='=&'
parm=($QUERY_STRING)
IFS=$saveIFS
Now you have this:
现在你有这个:
parm[0]=a
parm[1]=123
parm[2]=b
parm[3]=456
parm[4]=c
parm[5]=ok
In Bash 4, which has associative arrays, you can do this (using the array created above):
在具有关联数组的 Bash 4 中,您可以这样做(使用上面创建的数组):
declare -A array
for ((i=0; i<${#parm[@]}; i+=2))
do
array[${parm[i]}]=${parm[i+1]}
done
which will give you this:
这会给你这个:
array[a]=123
array[b]=456
array[c]=ok
Edit:
编辑:
To use indirection in Bash 2 and later (using the parm
array created above):
要在 Bash 2 及更高版本中使用间接(使用parm
上面创建的数组):
for ((i=0; i<${#parm[@]}; i+=2))
do
declare var_${parm[i]}=${parm[i+1]}
done
Then you will have:
那么你将拥有:
var_a=123
var_b=456
var_c=ok
You can access these directly:
您可以直接访问这些:
echo $var_a
or indirectly:
或间接:
for p in a b c
do
name="var$p"
echo ${!name}
done
If possible, it's better to avoid indirectionsince it can make code messy and be a source of bugs.
如果可能,最好避免间接,因为它会使代码变得混乱并成为错误的来源。
回答by ghostdog74
you can break $QUERY
down using IFS
. For example, setting it to &
您可以$QUERY
使用IFS
. 例如,将其设置为&
$ QUERY="a=123&b=456&c=ok"
$ echo $QUERY
a=123&b=456&c=ok
$ IFS="&"
$ set -- $QUERY
$ echo
a=123
$ echo
b=456
$ echo
c=ok
$ array=($@)
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; echo ; done
a 123
b 456
c ok
And you can save to a hash/dictionary in Bash 4+
您可以在 Bash 4+ 中保存到哈希/字典
$ declare -A hash
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; hash[]=; done
$ echo ${hash["b"]}
456
回答by bolt
Please don't use the evil eval junk.
请不要使用邪恶的评估垃圾。
Here's how you can reliably parse the string and get an associative array:
以下是如何可靠地解析字符串并获得关联数组的方法:
declare -A param
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
param["$key"]=$value
done <<<"${QUERY_STRING}&"
If you don't like the key check, you could do this instead:
如果你不喜欢密钥检查,你可以这样做:
declare -A param
while IFS='=' read -r -d '&' key value; do
param["$key"]=$value
done <<<"${QUERY_STRING:+"${QUERY_STRING}&"}"
Listing all the keys and values from the array:
列出数组中的所有键和值:
for key in "${!param[@]}"; do
echo "$key: ${param[$key]}"
done
回答by Simon
I packaged the sed command up into another script:
我将 sed 命令打包到另一个脚本中:
$cat getvar.sh
$cat getvar.sh
s='s/^.*''=\([^&]*\).*$//p'
echo $QUERY_STRING | sed -n $s | sed "s/%20/ /g"
and I call it from my main cgi as:
我从我的主 cgi 中调用它为:
id=`./getvar.sh id`
ds=`./getvar.sh ds`
dt=`./getvar.sh dt`
...etc, etc - you get idea.
...等等,等等 - 你明白了。
works for me even with a very basic busybox appliance (my PVR in this case).
即使使用非常基本的busybox设备(在这种情况下我的PVR)也适用于我。
回答by Tai Paul
To converts the contents of QUERY_STRING into bash variables use the following command:
要将 QUERY_STRING 的内容转换为 bash 变量,请使用以下命令:
eval $(echo ${QUERY_STRING//&/;})
The inner step, echo ${QUERY_STRING//&/;}
, substitutes all ampersands with semicolons producing a=123;b=456;c=ok which the eval
then evaluates into the current shell.
内部步骤echo ${QUERY_STRING//&/;}
用分号替换所有 & 符号,产生 a=123;b=456;c=ok,eval
然后将其计算到当前 shell 中。
The result can then be used as bash variables.
然后可以将结果用作 bash 变量。
echo $a
echo $b
echo $c
The assumptions are:
假设是:
- values will never contain '&'
- values will never contain ';'
- QUERY_STRING will never contain malicious code
- 值永远不会包含“&”
- 值永远不会包含“;”
- QUERY_STRING 永远不会包含恶意代码
回答by peterh - Reinstate Monica
While the accepted answer is probably the most beautiful one, there might be cases where security is super-important, and it needs to be also well-visible from your script.
虽然公认的答案可能是最漂亮的答案,但在某些情况下,安全性可能非常重要,并且它也需要从您的脚本中清晰可见。
In such a case, first I wouldn't use bash for the task, but if it should be done on some reason, it might be better to avoid these new array - dictionary features, because you can't be sure, how exactly are they escaped.
在这种情况下,首先我不会使用 bash 来完成任务,但是如果出于某种原因应该这样做,最好避免使用这些新的数组 - 字典功能,因为您无法确定究竟是如何他们逃走了。
In this case, the good old primitive solutions might work:
在这种情况下,好的旧原始解决方案可能会起作用:
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
This iterates on the name-value pairs of the QUERY_STRING
, and there is no way to circumvent it with any tricky escape sequence - the "
is a very strong thing in bash, except a single variable name substitution, which is fully controlled by us, nothing can be tricked.
这对 的名称-值对进行迭代QUERY_STRING
,并且没有办法用任何棘手的转义序列来规避它 -"
在 bash 中这是一个非常强大的东西,除了单个变量名称替换,它完全由我们控制,没有什么可以被骗。
Furthermore, you can inject your own processing code into "# ...
". This enables you to allow only your own, well-defined (and, ideally, short) list of the allowed variable names. Needless to say, LD_PRELOAD
shouldn't be one of them. ;-)
此外,您可以将自己的处理代码注入“ # ...
”。这使您能够只允许您自己的、明确定义的(理想情况下是简短的)允许变量名称列表。不用说,LD_PRELOAD
不应该是其中之一。;-)
Furthermore, no variable will be exported, and exclusively QS
, nameval
, name
and val
is used.
此外,没有变量将被导出,并专门QS
,nameval
,name
和val
使用。
回答by Petras L
I would simply replace the & to ;. It will become to something like:
我会简单地将 & 替换为 ;。它会变成这样:
a=123;b=456;c=ok
So now you need just evaluate and read your vars:
所以现在你只需要评估和阅读你的变量:
eval `echo "${QUERY_STRING}"|tr '&' ';'`
echo $a
echo $b
echo $c
回答by badc0de
Following the correct answer, I've done myself some changes to support array variables like in this other question. I added also a decode function of which I can not find the author to give some credit.
按照正确答案,我对自己进行了一些更改以支持数组变量,例如在另一个问题中。我还添加了一个解码功能,我找不到作者来给予一些信任。
Code appears somewhat messy, but it works. Changes and other recommendations would be greatly appreciated.
代码看起来有些凌乱,但它有效。将不胜感激更改和其他建议。
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
回答by user3292713
回答by starfry
To bring this up to date, if you have a recent Bash version then you can achieve this with regular expressions:
为了更新这一点,如果您有最新的 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
If you don't want to use associative arrays then just change the penultimate line to do what you want. For each iteration of the loop the parameter is in ${BASH_REMATCH[1]}
and its value is in ${BASH_REMATCH[2]}
.
如果您不想使用关联数组,那么只需更改倒数第二行即可执行您想要的操作。对于循环的每次迭代,参数都在${BASH_REMATCH[1]}
,其值在${BASH_REMATCH[2]}
。
Here is the same thing as a function in a short test script that iterates over the array outputs the query string's parameters and their values
这与迭代数组的简短测试脚本中的函数相同,输出查询字符串的参数及其值
#!/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 "+=([${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
Note the parameters end up in the array in reverse order (it's associative so that shouldn't matter).
请注意,参数以相反的顺序出现在数组中(它是关联的,因此无关紧要)。