Как проанализировать $ QUERY_STRING из CGI-скрипта bash - PullRequest
20 голосов
/ 13 октября 2010

У меня есть скрипт bash, который используется в CGI. CGI устанавливает переменную среды $ QUERY_STRING, читая все, что находится после ? в URL. Например, 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?

Возможно, я просто использую цикл for некоторого вида, но было бы еще лучше, если бы скрипт был достаточно умен, чтобы автоматически определять каждый параметр и, возможно, построить массив, который выглядит примерно так:

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

Как я мог написать код для этого?

Ответы [ 13 ]

40 голосов
/ 13 октября 2010

Попробуйте это:

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

Редактировать:

Чтобы использовать косвенное обращение в Bash 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

Если возможно, лучше избегать косвенного обращения , так как это может привести к путанице в коде и стать источником ошибок.

15 голосов
/ 13 октября 2010

вы можете сломать $QUERY, используя IFS.Например, установив его на &

$ 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

И вы можете сохранить в хэш / словарь в Bash 4 +

$ declare -A hash
$ for i in "${array[@]}"; do IFS="=" ; set -- $i; hash[$1]=$2; done
$ echo ${hash["b"]}
456
3 голосов
/ 18 июля 2017

Чтобы преобразовать содержимое QUERY_STRING в переменные bash, используйте следующую команду:

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

Внутренний шаг, echo ${QUERY_STRING//&/;}, заменяет все амперсанды точками с запятой, получая a = 123; b = 456; c = ok, который eval затем оценивает в текущую оболочку.

Результат может быть использован в качестве переменных bash.

echo $a
echo $b
echo $c

Допущения:

  • значения никогда не будут содержать '&'
  • значения никогда не будут содержать ';'
  • QUERY_STRING никогда не будет содержать вредоносный код
3 голосов
/ 13 апреля 2016

Пожалуйста, не используйте зловредный мусорный мусор.

Вот как вы можете надежно разобрать строку и получить ассоциативный массив:

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
2 голосов
/ 08 марта 2014

Я упаковал команду 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 в данном случае).

1 голос
/ 26 марта 2016

Я бы просто заменил & to;. Это станет чем-то вроде:

a=123;b=456;c=ok

Так что теперь вам нужно просто оценить и прочитать ваши переменные:

eval `echo "${QUERY_STRING}"|tr '&' ';'`
echo $a
echo $b
echo $c
1 голос
/ 10 февраля 2014

Хороший способ обработки строк запроса CGI - использовать Haserl , который действует как оболочка для вашего скрипта Bash cgi и предлагает удобный и безопасный анализ строки запроса.

0 голосов
/ 09 октября 2018

Хотя принятый ответ, вероятно, самый красивый, могут быть случаи, когда безопасность очень важна, и она также должна быть хорошо видна из вашего сценария.

В таком случае, во-первых, я бы не использовал 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

Это перебирает пары имя-значение QUERY_STRING, и нет способа обойти его любой хитрой escape-последовательностью - " - очень сильная вещь в bash, за исключением подстановки имени одной переменной, который полностью контролируется нами, ничто не может быть обманутым.

Кроме того, вы можете добавить свой собственный код обработки в "# ...". Это позволяет вам разрешить только свой собственный, четко определенный (и, в идеале, короткий) список разрешенных имен переменных. Само собой разумеется, LD_PRELOAD не должен быть одним из них. ; -)

Кроме того, никакие переменные не будут экспортироваться, и будут использоваться исключительно QS, nameval, name и val.

0 голосов
/ 02 июня 2018

Для всех тех, кто не мог заставить его работать с опубликованными ответами (как я), этот парень понял это.

К сожалению, не могу поднять свой пост ...

Позвольте мне очень быстро опубликовать здесь код:

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

Надеюсь, это кому-нибудь поможет.

Приветствия

0 голосов
/ 25 марта 2018

@ giacecco

Чтобы включить в регулярное выражение дефис, вы можете изменить две строки как таковые в ответе @ starfry.

Изменить эти две строки:

  local re1='^(\w+=\w+)&?'
  local re2='^(\w+)=(\w+)$'

На эти две строки:

  local re1='^(\w+=(\w+|-|)+)&?'
  local re2='^(\w+)=((\w+|-|)+)$'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...