2010-10-12 149 views
16

我有一個正在CGI中使用的bash腳本。 CGI通過讀取URL中的?之後的所有內容來設置$ QUERY_STRING環境變量。例如,http://example.com?a=123&b=456&c=ok集合QUERY_STRING=a=123&b=456&c=ok

某處我發現下面的醜陋:

b=$(echo "$QUERY_STRING" | sed -n 's/^.*b=\([^&]*\).*$/\1/p' | sed "s/%20/ /g")

將設置$ B到任何在$ QUERY_STRING發現b。但是,我的腳本已經增長到超過十個輸入參數。有沒有更簡單的方法來自動將$ QUERY_STRING中的參數轉換爲bash可用的環境變量?

也許我會只使用一個某種類型的循環,但它會是更好的,如果劇本足夠聰明,能夠自動檢測各項參數,也許建立一個數組,看起來是這樣的:

 
${parm[a]}=123 
${parm[b]}=456 
${parm[c]}=ok 

我怎麼能寫代碼來做到這一點?

+0

我只注意到我確實停留在猛砸3.有沒有人有這將不涉及關聯數組簡單,安全的解決方案? – User1 2010-10-13 16:30:56

+1

請參閱我編輯的關於關聯數組的替代答案(也請務必閱讀我鏈接到的頁面([BashFAQ/006](http://mywiki.wooledge.org/BashFAQ/006))。 – 2010-10-19 01:32:26

+0

此鏈接將有助於你很容易解決你的問題 http://stackoverflow.com/questions/17021640/how-to-extract-the-data-using-sed-command – amar 2013-06-11 05:23:34

回答

30

試試這個:

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中,它具有關聯數組,就可以做到這一點(使用上面創建的陣列):

declare -A array 
for ((i=0; i<${#parm[@]}; i+=2)) 
do 
    array[${parm[i]}]=${parm[i+1]} 
done 

這將給你:

array[a]=123 
array[b]=456 
array[c]=ok 

編輯:

要在擊使用間接2和更高版本(使用上面創建的parm陣列):

for ((i=0; i<${#parm[@]}; i+=2)) 
do 
    declare var_${parm[i]}=${parm[i+1]} 
done 

然後,你將有:

var_a=123 
var_b=456 
var_c=ok 

您可以直接訪問這些:

echo $var_a 

或間接:

for p in a b c 
do 
    name="var$p" 
    echo ${!name} 
done 

如果可能的話,最好還是avoid indirection,因爲它可以使代碼凌亂併成爲錯誤的來源。

+1

對於'parm'數組生成+1。但是呈現給該數組的所有方法都無法正確處理重複的鍵。每次出現都會覆蓋前一個。例如,a = 1&a = 2&a = x會導致parm [a] = x – MestreLion 2011-08-09 20:44:01

+0

@MestreLion:您可以添加邏輯來處理重複鍵的可能性,但您需要決定如何處理它們。你可以先做先例或先行先試或積累一些方法。 – 2011-08-11 22:14:08

+1

'parm =($ QUERY_STRING)'將'$ QUERY'擴展所產生的單詞歸入globbing,這可能是不希望的。 一個更強大的替代方案,同樣可以節省和恢復'$ IFS'的麻煩:'IFS ='&='read -ra parm <<<「$ QUERY_STRING」' 最好不要使用全大寫shell-變量名稱,以[避免與環境變量和特殊shell變量的衝突](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_01)。 – mklement0 2016-08-03 03:36:08

14

您可以使用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=([email protected]) 

$ for i in "${array[@]}"; do IFS="=" ; set -- $i; echo $1 $2; done 
a 123 
b 456 
c ok 

你也可以保存到一個散列/字典中的Bash 4+

$ declare -A hash 
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; hash[$1]=$2; done 
$ echo ${hash["b"]} 
456 
+1

+1爲'set - $ var'技巧..非常整潔;) – MestreLion 2011-08-09 20:36:09

+0

除非你依賴分詞,請將你的變量引用重複引用。 請注意,'set - $ QUERY'使'$ QUERY'中的單詞受制於globbing,這可能是不受歡迎的。最好不要使用全大寫的shell變量名[以避免與環境變量和特殊shell變量衝突](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_01)。 – mklement0 2016-08-03 03:31:46

+0

@ mklement0:這依賴於單詞分割,特別是在分割'&'時。由於查詢字符串是經過網址編碼的,因此全局搜索不是問題。 – MSalters 2017-06-29 11:37:38

1

處理CGI查詢字符串的一個好方法是使用Haserl,它充當Bash cgi腳本的包裝,並提供方便和安全的查詢字符串解析。

2

我打包sed命令成另一個腳本:

$貓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)也適用於我。

+1

用於未加引號的$ QUERY_STRING。你真的,真的必須在變量周圍使用雙引號。 – tripleee 2015-02-23 08:50:06

0

遵循正確的答案,我自己做了一些更改以支持數組變量,如this other question。我還添加了一個解碼功能,我找不到作者給予某些功勞。

代碼看起來有些雜亂,但它的工作原理。更改和其他建議將不勝感激。

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 
0

爲了把這個最新的,如果你有一個最新的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_REMATCH[1]}中,其值在${BASH_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   

注意的參數以相反的順序在陣列中結束(這是聯想,所以應該不重要)。

+0

@starfy謝謝你,但它不適用於一些適用於參數值的字符,例如:簡單的連字符「 - 」。當解析這些參數時 - 例如p = foo-bar - 只返回值的第一部分(foo)。 – giacecco 2017-02-19 11:40:24

0

爲什麼不這樣

$ 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 
+0

這是一種危險 - 腳本中的任何*變量都可以被這些參數重寫。 – 2015-12-18 04:14:07

1

我想簡單地更換&來。這將成爲類似:

a=123;b=456;c=ok 

所以,現在你只需要評估和閱讀您的增值經銷商:

eval `echo "${QUERY_STRING}"|tr '&' ';'` 
echo $a 
echo $b 
echo $c 
+0

這不僅是一個安全風險,而且也是脆弱的,因爲值本身可以包含';'或者以'〜'開頭。 – mklement0 2016-08-03 03:40:56

3

請不要使用邪惡的eval垃圾。

這裏是你如何能夠可靠地解析字符串,並得到一個關聯數組:

declare -A param 
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do 
    param["$key"]=$value 
done <<<"${QUERY_STRING}&" 

如果你不喜歡的重點檢查,你可以這樣做,而不是:

declare -A param 
while IFS='=' read -r -d '&' key value; do 
    param["$key"]=$value 
done <<<"${QUERY_STRING:+"${QUERY_STRING}&"}" 

列出所有來自陣列的鍵和值:

for key in "${!param[@]}"; do 
    echo "$key: ${param[$key]}" 
done 
1

要將QUERY_STRING的內容轉換爲ba SH變量使用下面的命令:

eval $(echo ${QUERY_STRING//&/;}) 

內步驟,echo ${QUERY_STRING//&/;},替換用分號製造= 123所有&符號; B = 456; C = OK其中eval然後評估到當前殼。

結果可以用作bash變量。

echo $a 
echo $b 
echo $c 

的假設是:

  • 值永遠不會包含 '&'
  • 值永遠不會包含 ';'
  • QUERY_STRING永遠不會包含惡意代碼