2010-10-12 19 views
16

Mam skrypt bash, który jest używany w CGI. CGI ustawia zmienną środowiskową $ QUERY_STRING, odczytując wszystko po adresie ?. Na przykład http://example.com?a=123&b=456&c=ok ustawia QUERY_STRING=a=123&b=456&c=ok.

Gdzieś znalazłem następujący brzydotę:

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

który będzie ustawiony $ b, aby cokolwiek zostało znalezione w $ QUERY_STRING dla b. Jednak mój skrypt ma już ponad dziesięć parametrów wejściowych. Czy istnieje prostszy sposób automatycznego przekonwertowania parametrów w $ QUERY_STRING na zmienne środowiskowe, które mogą być użyte przez bash?

Może ja po prostu użyć pętli for jakiegoś, ale byłoby jeszcze lepiej, gdyby scenariusz był na tyle silny, aby automatycznie wykryć każdy parametr a może zbudować tablicę, która wygląda mniej więcej tak:

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

Jak mogę napisać kod, aby to zrobić?

+0

Właśnie zauważyłem, że jestem naprawdę zatrzymany na Bash 3. Czy ktoś ma proste, bezpieczne rozwiązanie, które nie będzie wiązać tablice asocjacyjne? – User1

+1

Zobacz moją zmienioną odpowiedź na alternatywę dla tablic asocjacyjnych (przeczytaj także stronę, do której się przyłączyłem ([BashFAQ/006] (http://mywiki.wooledge.org/BashFAQ/006)). –

+0

ten link pomoże Ci rozwiązać problem łatwo http://stackoverflow.com/questions/17021640/how-to-extract-the-data-using-sed-command – amar

Odpowiedz

30

Spróbuj tego:

saveIFS=$IFS 
IFS='=&' 
parm=($QUERY_STRING) 
IFS=$saveIFS 

Teraz masz to:

parm[0]=a 
parm[1]=123 
parm[2]=b 
parm[3]=456 
parm[4]=c 
parm[5]=ok 

W Bash 4, który ma asocjacyjnych, można to zrobić (za pomocą tablicy utworzonej powyżej):

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

który dasz to:

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

Edit:

Aby korzystać zadnie w bash 2 i nowsze (przy użyciu macierzy parm utworzonej powyżej):

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

Wtedy masz:

var_a=123 
var_b=456 
var_c=ok 

Można uzyskać do nich dostęp bezpośrednio:

echo $var_a 

lub pośrednio:

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

Jeśli to możliwe, to lepiej avoid indirection ponieważ może to uczynić kod niechlujny i być źródłem błędów.

+1

+1 dla generowania tablicy 'parm'. Ale wszystkie metody przedstawione w pętli tej tablicy nie obsługują poprawnie powtarzających się kluczy. Każde wystąpienie spowoduje nadpisanie poprzedniego. Na przykład a = 1 & a = 2 & a = x dałoby parm [a] = x – MestreLion

+0

@MestreLion: Możesz dodać logikę, aby poradzić sobie z możliwością powtarzania kluczy, ale musisz zdecydować, jak chcesz sobie z nimi poradzić. Możesz zrobić pierwszy precedens lub ostatni precedens lub pewną metodę akumulacji. –

+1

'parm = ($ QUERY_STRING)' poddaje wyrazy pochodzące z rozszerzenia '$ QUERY' do globowania, co jest prawdopodobnie niepożądane. Bardziej niezawodna alternatywa, która również oszczędza kłopotów związanych z zapisywaniem i przywracaniem '$ IFS':' IFS = '& =' odczyt -ra parm <<< "$ QUERY_STRING" ' Lepiej nie używać całej wielkiej litery powłoki- nazwy zmiennych w celu [unikania konfliktów ze zmiennymi środowiskowymi i specjalnymi zmiennymi powłoki] (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_01). – mklement0

14

można złamać $QUERY w dół przy użyciu IFS. Na przykład, ustawienie go na &

$ 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 

I można zaoszczędzić do mieszania/słownik w Bash 4+

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

+1 dla 'zestaw - $ var' sztuczki .. bardzo schludne;) – MestreLion

+0

Oprócz sytuacji, w których polegasz na dzieleniu wyrazów, proszę dwukrotnie podać swoje referencje zmienne. Należy zauważyć, że 'set - $ QUERY' powoduje, że słowa w' $ QUERY' podlegają globalizacji, co jest prawdopodobnie niepożądane. Lepiej nie używać nazw wielkiej litery powłoki, aby [uniknąć konfliktów ze zmiennymi środowiskowymi i specjalnymi zmiennymi powłoki] (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_01). – mklement0

+0

@ mklement0: To opiera się na dzieleniu słów, w szczególności na dzieleniu w '&'. Zagnieżdżanie nie stanowi problemu, ponieważ ciąg kwerendy jest zakodowany za pomocą adresu URL. – MSalters

1

Dobrym sposobem na obsługę łańcuchów zapytań CGI jest użycie Haserl, który działa jako wrapper do skryptu Bash cgi i oferuje wygodne i bezpieczne analizowanie łańcucha zapytań.

2

I pakowane polecenia sed się do innego skryptu:

$ cat getvar.sh

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

i wzywam go z moim głównym CGI jak:

id=`./getvar.sh id` 
ds=`./getvar.sh ds` 
dt=`./getvar.sh dt` 

... itd. - masz pomysł.

działa dla mnie nawet z bardzo podstawowym urządzeniem busybox (w tym przypadku moim PVR).

+1

dla niecytowanego "$ QUERY_STRING". Naprawdę, naprawdę musisz używać podwójnych cudzysłowów wokół zmiennej. – tripleee

0

Po poprawnej odpowiedzi, zrobiłem sobie kilka zmian w obsłudze zmiennych tablicowych, takich jak w this other question. Dodałem także funkcję dekodowania, której nie mogę znaleźć autora, aby dać trochę kredytu.

Kod wydaje się nieco brudny, ale działa. Zmiany i inne zalecenia będą bardzo mile widziane.

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

Aby to przynieść aktualne, czy masz najnowszą wersję Bash to można to osiągnąć z wyrażeń regularnych:

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 

Jeśli nie chcesz korzystać z tablic asocjacyjnych potem po prostu zmiana przedostatnia linia, aby zrobić to, co chcesz. Dla każdej iteracji pętli parametr ten ma wartość ${BASH_REMATCH[1]}, a jego wartość to ${BASH_REMATCH[2]}.

Oto samo jak funkcja w krótkim skryptu testowego iteracje nad tablicy wyprowadza parametrów łańcucha zapytania, a ich wartości

#!/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   

zanotować parametry skończyć w tablicy w odwrotnej kolejności (to asocjacyjny, więc nie powinno to mieć znaczenia).

+0

@strasz za to dziękuję, ale nie działa z kilkoma znakami, które są dopuszczalne w wartościach parametrów, np. prosty łącznik "-". Podczas analizowania takich parametrów - np. p = foo-bar - zwracana jest tylko pierwsza część wartości (foo). – giacecco

0

dlaczego nie ten

$ echo "${QUERY_STRING}" 
    name=carlo&last=lanza&city=pfungen-CH 
    $ saveIFS=$IFS 
    $ IFS='&' 
    $ eval $QUERY_STRING 
    $ IFS=$saveIFS 

teraz masz to

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

To jest trochę niebezpieczne - * dowolna * zmienna ze skryptu może zostać przepisana przez te parametry. –

1

Chciałbym po prostu zastąpić & do;. Stanie się coś takiego:

a=123;b=456;c=ok 

Więc teraz trzeba tylko czytać i oceniać vars:

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

Jest to nie tylko zagrożenie bezpieczeństwa, ale także kruche, biorąc pod uwagę, że same wartości mogą zawierać ';' lub zaczynają się od '~'. – mklement0

3

Proszę nie używać eval zło śmieci.

Oto jak można wiarygodnie analizować ciąg i uzyskać tablicę asocjacyjną:

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

Jeśli nie podoba ci się kluczową czek, można to zrobić w zamian:

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

listingu wszystko klucze i wartości z tablicy:

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

Konwertuje zawartość QUERY_STRING na ba Zmienne sh wykorzystywać następujące polecenia:

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

Wewnętrzny krok echo ${QUERY_STRING//&/;} substytuty wszystkie ampersandy średnikami wytwarzania = 123, b = 456 c = OK którym eval następnie ocenia się w obecnej powłoki.

Wynik może być następnie użyty jako zmienne bash.

echo $a 
echo $b 
echo $c 

Założenia są następujące:

  • wartości nigdy nie będzie zawierać '&'
  • wartości nigdy nie będzie zawierać ';'
  • QUERY_STRING nie będzie zawierać złośliwy kod
Powiązane problemy