I got a config file called user.conf that contains this list:
USER1_USERNAME="John"
#USER2_USERNAME="Mike"
USER3_USERNAME="David"
USER4_USERNAME="James"
USER5_USERNAME="Jenny"
Notice that user can comment out that line USER2_USERNAME so, only 4 users are used in source file in bash script test.sh here:
#!/bin/bash
#source the username
source "user.conf"
n=1
while :; do
((n++))
if [ -n "${USER${n}_USERNAME}"]; then
echo "This variable USER${n}_USERNAME is set: ${USER${n}_USERNAME}"
else
echo "This variable USER${n}_USERNAME is not set"
fi
done
I want to display which variable is set with its value and skip the commented variables but at this point my code just display an error:
./test.sh: line 8: ${USER${n}_USERNAME}: bad substitution
Is it possible to loop that variable like above?
Also, user can define much more USER in that user.conf, for example
USER6_USERNAME="George "
Expected output:
This variable USER1_USERNAME is set to John
This variable USER2_USERNAME is not set
This variable USER3_USERNAME is set to David
This variable USER4_USERNAME is set to James
This variable USER5_USERNAME is set to Jenny
You can use bash variable substitution like this:
#!/usr/bin/env bash
source "user.conf"
for i in "${!USER#}"
do
echo $i ${!i}
done
Like your code, this first sources the users file, as it looks like a bash script. This of course has the potentially dangerous side effect that any other code in user.conf runs as well, so be careful and don't let strangers modify that file.
Then it uses ${!var#}, which expands to the names of variables whose names begin with a prefix, here "var", or for you "USER". You could also use ${!var*}, depending on whether you want all values in one quoted variable or multiple ones. See shell parameter expansion for details.
The whole approach is tied to a common prefix for your config variables. In this case, you'll also see $USER in the output, which is the name of the currently logged in user. You can filter that with e.g., grep or a simple if [ "$i" != "USER" ] in the loop.
If you want undefined variables as well, sourcing the users file may not be a good solution. You could instead read the file line by line and check for a leading #:
#!/usr/bin/env bash
set -eu
while IFS= read -r line
do
var=$(echo "$line" | cut -d '=' -f 1)
name=$(echo "$line" | cut -d '=' -f 2)
if [[ "$var" =~ ^# ]]
then
var=$(echo "$var" | cut -c 2-)
echo "The variable $var is not set"
else
echo "The variable $var is set to $name"
fi
done
Output:
bash users.sh < users.conf
The variable USER1_USERNAME is set to "John"
The variable USER2_USERNAME is not set
The variable USER3_USERNAME is set to "David"
The variable USER4_USERNAME is set to "James"
The variable USER5_USERNAME is set to "Jenny"
However, this approach is brittle as it doesn't understand bash syntax. A leading space would be fine when sourcing, but would trip the comment detection on this code. Variables in user names would not get expanded, which may or may not be a good thing.
I liked what Robert suggested. But IMHO whole concept of your script is wrong. This should be done with array(s) not dynamic vars.
users=(
"John"
"Mike"
"David"
"James"
"Jenny"
)
And than you just loop over that array
for user in "${users[#]}"; { echo "$user"; }
If you want to make this config editable by users this can also be done, thay can comment, delete or add items in array:
users=(
"John"
# "David"
"James"
"Jenny"
"Lenny"
)
I also found my own way to deal with this by modifying the source file user.conf to have suffix at the end:
#user.conf
USER_USERNAME1="John"
#USER_USERNAME2="Mike"
USER_USERNAME3="David"
USER_USERNAME4="James"
USER_USERNAME5="Jenny"
#!/bin/bash
source "user.conf"
usernames="${!USER_USERNAME#}"
username_count=$(echo "${usernames}" | wc -w)
count=1
while [[ ${count} -le ${username_count} ]]; do
typeset -n "username"="USER_USERNAME${count}"
if [ -n "${username}" ]; then
echo "The variable USER_USERNAME${count} is set to ${username}"
else
echo "The variable USER_USERNAME${count} is not set"
fi
((count = count + 1))
done
Since it's restricted to put this in question, I post it here.
I have a file like this format:
a;b;c
e;d;f
how can I use shell to read the file detail information into variables?
I would have 6 variables to store the data.
more detailed information for this is that as the following shows:
I have written a script:
#!/bin/sh
unset ret
ret=0
if [ "$#" -ne 3 ]; then
logger -p err "Usage: $0 eth_name rule_file table_name"
ret=1
exit ret
fi
OFS=$IFS # store field separator
IFS=";" # define field separator
eth_name=$1 # ethernet device name
rule_file=$2 # input file name
table_name=$3 # lookup table name
logger -p notice "$0 $eth_name $rule_file $table_name"
unset a # reference to line array
unset i j # index
unset m n # dimension
### read route configuration
i=0
while read line
do
a=A$i
unset $a
declare -a $a='($line)'
i=$((i+1))
done < $rule_file
# store number of lines
m=$i
# function for apply route
add_route()
{
if [ "source" = "$1" ]; then
src_address=$(ifconfig $eth_name | sed -n 's/.*inet addr:\([0-9.]\+\)\s.*/\1/p')
ip rule add from $src_address lookup $table_name
ret=$?
logger -p notice "ip rule add from $src_address lookup $table_name $ret"
elif [ "default" = "$1" ]; then
ip route add default via $2 table $table_name
ret=$?
logger -p notice "ip route add default via $2 table $table_name $ret"
else
ipaddress_range=$1
gateway_ipaddress=$2
ip route add $ipaddress_range via $gateway_ipaddress dev $eth_name table $table_name
ret=$?
logger -p notice "ip route add $ipaddress_range via $gateway_ipaddress dev $eth_name table $table_name $ret"
fi
}
### apply route configuration
for ((i=0; i < $m; i++))
do
a=A$i
# get line size
# double escape '\\' for sub shell '``' and 'echo'
p0=`eval echo \\${$a[0]}`
p1=`eval echo \\${$a[1]}`
add_route $p0 $p1
done
IFS=$OFS
the rule file's format is as the following shows:
source;
default_route;172.20.5.192/26
default_gateway;172.20.5.254
172.17.23.64/26;172.20.5.254
172.31.252.0/24;172.20.5.254
172.31.254.0/24;172.20.5.254
10.217.1.0/24;172.20.5.254
10.217.2.0/24;172.20.5.254
this script is working normally under the bash environment, now my linux system is not having bash now, this script is not working now, how to change the script to make the script running?
the function for this script is very simple, write every line into the linux system's ip rule and ip route. need to throw 3 variables to make the script running.
You can easily achieve it with read.
#!/bin/bash
while IFS=";" read -r var1 var2 var3; do
command
done <file
exit 0
where:
IFS is your delimiter
varN are your vars, you can use any name you want
N.B. If you need stdin you need to use a file descriptor:
#!/bin/bash
exec 3<file
while IFS=";" read -r var1 var2 var3 <&3; do
command
done
exec 3>&-
exit 0
Further readings here.
N.B.#2 The command read is not the faster solution, usually after 5k lines lose 100 ms compared to other tools (E.G. awk).
The following parses your original sample text and saves fields to sequentially numbered variables, stripping values out of lines using parameter expansion, and doesn't care how many fields you have per line.
#!/bin/sh
i=0
while read line; do
while [ -n "$line" ]; do
eval this_$((i=i+1))="\${line%%;*}"
last="$line"
line="${line#*;}"
[ "$last" = "$line" ] && break
done
done < input.txt
I've tested this successfully with both bash and FreeBSD's /bin/sh (which is based on ash). (Note that FreeBSD's /bin/sh doesn't seem to like arithmetic expressions like $((i++)), but both shells I tested are fine with the notation above.)
But from the look of the script you updated your question with, this isn't what you need. It seems that you have input data with:
one record per line, and
two fields, separated by semicolons.
But I wonder if you even need to store things in variables. It seems to me you'd be looking more for the following type of structure.
#!/bin/sh
...
while IFS=";" read range gateway; do
case "$range" in
source)
: Note that $gateway is blank
;;
default_route)
: do something
;;
default_gateway)
: do something else
;;
[0-9]*)
ip route add "$range" via "$gateway" dev "$eth_name" table "$table_name"
;;
*)
printf 'ERROR: unknown range in rule file: %s\n' "$range" >&2
;;
esac
done < $rule_file
Additional input validation wouldn't hurt.
I want to read the following variables from a data file in bash.
#/tmp/input.dat
$machie=1234-567*890ABC
$action=REPLACE
$location=test_location
Thanks for your help.
Tas
#!/usr/bin/env bash
case $BASH_VERSION in
''|[0-3].*) echo "ERROR: Bash 4.0 or newer is required" >&2; exit 1;;
esac
# read input filename from command line, default to "/tmp/input.dat"
input_file=${1:-/tmp/input.dat}
declare -A vars=()
while IFS= read -r line; do
[[ $line = "#"* ]] && continue # Skip comments in input
[[ $line = *=* ]] || continue # Skip lines not containing an "="
line=${line#'$'} # strip leading "$"
key=${line%%=*} # remove everything after first "=" to get key
value=${line#*=} # remove everything before first "=" to get value
vars[$key]=$value # add key/value pair to associative array
done <"$input_file"
# print the variables we read for debugging purposes
declare -p vars >&2
echo "Operation is ${vars[action]}; location is ${vars[location]}" >&2
See:
BashFAQ #1 - How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?
BashFAQ #6 - How can I use variable variables (indirect variables, pointers, references) or associative arrays?; here, we're using associative arrays, but you could use the same technique to assign directly to named variables.[1]
Parameter expansion, the syntax used for isolating the "key" and "value" sections of each line; also covered in BashFAQ #100.
[1] - Note that if you aren't going to use associative arrays, as suggested in this answer, for security reasons it's best to use a prefixed namespace: printf -v "var_$key" %s "$value" -- generating variable names you would dereference as $var_action or $var_location -- is much safer than printf -v "$key" %s "$value", as the former ensures that your data file can't overwrite a security-critical environment variable such as PATH or LD_PRELOAD, by way of causing such attempts to harmlessly set $var_PATH or $var_LD_PRELOAD.
I'm attempting to read a config file that is formatted as follows:
USER = username
TARGET = arrows
I realize that if I got rid of the spaces, I could simply source the config file, but for security reasons I'm trying to avoid that. I know there is a way to read the config file line by line. I think the process is something like:
Read lines into an array
Filter out all of the lines that start with #
search for the variable names in the array
After that I'm lost. Any and all help would be greatly appreciated. I've tried something like this with no success:
backup2.config>cat ~/1
grep '^[^#].*' | while read one two;do
echo $two
done
I pulled that from a forum post I found, just not sure how to modify it to fit my needs since I'm so new to shell scripting.
http://www.linuxquestions.org/questions/programming-9/bash-shell-program-read-a-configuration-file-276852/
Would it be possible to automatically assign a variable by looping through both arrays?
for (( i = 0 ; i < ${#VALUE[#]} ; i++ ))
do
"${NAME[i]}"=VALUE[i]
done
echo $USER
Such that calling $USER would output "username"? The above code isn't working but I know the solution is something similar to that.
The following script iterates over each line in your input file (vars in my case) and does a pattern match against =. If the equal sign is found it will use Parameter Expansion to parse out the variable name from the value. It then stores each part in it's own array, name and value respectively.
#!/bin/bash
i=0
while read line; do
if [[ "$line" =~ ^[^#]*= ]]; then
name[i]=${line%% =*}
value[i]=${line#*= }
((i++))
fi
done < vars
echo "total array elements: ${#name[#]}"
echo "name[0]: ${name[0]}"
echo "value[0]: ${value[0]}"
echo "name[1]: ${name[1]}"
echo "value[1]: ${value[1]}"
echo "name array: ${name[#]}"
echo "value array: ${value[#]}"
Input
$ cat vars
sdf
USER = username
TARGET = arrows
asdf
as23
Output
$ ./varscript
total array elements: 2
name[0]: USER
value[0]: username
name[1]: TARGET
value[1]: arrows
name array: USER TARGET
value array: username arrows
First, USER is a shell environment variable, so it might be better if you used something else. Using lowercase or mixed case variable names is a way to avoid name collisions.
#!/bin/bash
configfile="/path/to/file"
shopt -s extglob
while IFS='= ' read lhs rhs
do
if [[ $lhs != *( )#* ]]
then
# you can test for variables to accept or other conditions here
declare $lhs=$rhs
fi
done < "$configfile"
This sets the vars in your file to the value associated with it.
echo "Username: $USER, Target: $TARGET"
would output
Username: username, Target: arrows
Another way to do this using keys and values is with an associative array:
Add this line before the while loop:
declare -A settings
Remove the declare line inside the while loop and replace it with:
settings[$lhs]=$rhs
Then:
# set keys
user=USER
target=TARGET
# access values
echo "Username: ${settings[$user]}, Target: ${settings[$target]}"
would output
Username: username, Target: arrows
I have a script which only takes a very limited number of settings, and processes them one at a time, so I've adapted SiegeX's answer to whitelist the settings I care about and act on them as it comes to them.
I've also removed the requirement for spaces around the = in favour of ignoring any that exist using the trim function from another answer.
function trim()
{
local var=$1;
var="${var#"${var%%[![:space:]]*}"}"; # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}"; # remove trailing whitespace characters
echo -n "$var";
}
while read line; do
if [[ "$line" =~ ^[^#]*= ]]; then
setting_name=$(trim "${line%%=*}");
setting_value=$(trim "${line#*=}");
case "$setting_name" in
max_foos)
prune_foos $setting_value;
;;
max_bars)
prune_bars $setting_value;
;;
*)
echo "Unrecognised setting: $setting_name";
;;
esac;
fi
done <"$config_file";
Thanks SiegeX. I think the later updates you mentioned does not reflect in this URL.
I had to edit the regex to remove the quotes to get it working. With quotes, array returned is empty.
i=0
while read line; do
if [[ "$line" =~ ^[^#]*= ]]; then
name[i]=${line%% =*}
value[i]=${line##*= }
((i++))
fi
done < vars
A still better version is .
i=0
while read line; do
if [[ "$line" =~ ^[^#]*= ]]; then
name[i]=`echo $line | cut -d'=' -f 1`
value[i]=`echo $line | cut -d'=' -f 2`
((i++))
fi
done < vars
The first version is seen to have issues if there is no space before and after "=" in the config file. Also if the value is missing, i see that the name and value are populated as same. The second version does not have any of these. In addition it trims out unwanted leading and trailing spaces.
This version reads values that can have = within it. Earlier version splits at first occurance of =.
i=0
while read line; do
if [[ "$line" =~ ^[^#]*= ]]; then
name[i]=`echo $line | cut -d'=' -f 1`
value[i]=`echo $line | cut -d'=' -f 2-`
((i++))
fi
done < vars
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=ok sets 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")
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?
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:
${parm[a]}=123
${parm[b]}=456
${parm[c]}=ok
How could I write code to do that?
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):
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):
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 indirection since it can make code messy and be a source of bugs.
you can break $QUERY down using IFS. For example, setting it to &
$ 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
And you can save to a hash/dictionary in Bash 4+
$ declare -A hash
$ for i in "${array[#]}"; do IFS="=" ; set -- $i; hash[$1]=$2; done
$ echo ${hash["b"]}
456
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
I packaged the sed command up into another script:
$cat getvar.sh
s='s/^.*'${1}'=\([^&]*\).*$/\1/p'
echo $QUERY_STRING | sed -n $s | sed "s/%20/ /g"
and I call it from my main cgi as:
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).
To converts the contents of QUERY_STRING into bash variables use the following command:
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.
The result can then be used as bash variables.
echo $a
echo $b
echo $c
The assumptions are:
values will never contain '&'
values will never contain ';'
QUERY_STRING will never contain malicious code
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.
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.
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. ;-)
Furthermore, no variable will be exported, and exclusively QS, nameval, name and val is used.
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
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
A nice way to handle CGI query strings is to use Haserl which acts as a wrapper around your Bash cgi script, and offers convenient and secure query string parsing.
To bring this up to date, if you have a recent Bash version then you can achieve this with regular expressions:
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]}.
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 "$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
Note the parameters end up in the array in reverse order (it's associative so that shouldn't matter).
why not this
$ echo "${QUERY_STRING}"
name=carlo&last=lanza&city=pfungen-CH
$ saveIFS=$IFS
$ IFS='&'
$ eval $QUERY_STRING
$ IFS=$saveIFS
now you have this
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
#giacecco
To include a hiphen in the regex you could change the two lines as such in answer from #starfry.
Change these two lines:
local re1='^(\w+=\w+)&?'
local re2='^(\w+)=(\w+)$'
To these two lines:
local re1='^(\w+=(\w+|-|)+)&?'
local re2='^(\w+)=((\w+|-|)+)$'
For all those who couldn't get it working with the posted answers (like me),
this guy figured it out.
Can't upvote his post unfortunately...
Let me repost the code here real quick:
#!/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>"
Hope this is of help for anybody.
Cheers
Actually I liked bolt's answer, so I made a version which works with Busybox as well (ash in Busybox does not support here string).
This code will accept key1 and key2 parameters, all others will be ignored.
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
One can use the bash-cgi.sh, which processes :
the query string into the $QUERY_STRING_GET key and value array;
the post request data (x-www-form-urlencoded) into the $QUERY_STRING_POST key and value array;
the cookies data into the $HTTP_COOKIES key and value array.
Demands bash version 4.0 or higher (to define the key and value arrays above).
All processing is made by bash only (i.e. in an one process) without any external dependencies and additional processes invoking.
It has:
the check for max length of data, which can be transferred to it's input,
as well as processed as query string and cookies;
the redirect() procedure to produce redirect to itself with the extension changed to .html (it is useful for an one page's sites);
the http_header_tail() procedure to output the last two strings of the HTTP(S) respond's header;
the $REMOTE_ADDR value sanitizer from possible injections;
the parser and evaluator of the escaped UTF-8 symbols embedded into the values passed to the $QUERY_STRING_GET, $QUERY_STRING_POST and $HTTP_COOKIES;
the sanitizer of the $QUERY_STRING_GET, $QUERY_STRING_POST and $HTTP_COOKIES values against possible SQL injections (the escaping like the mysql_real_escape_string php function does, plus the escaping of # and $).
It is available here:
https://github.com/VladimirBelousov/fancy_scripts
This works in dash using for in loop
IFS='&'
for f in $query_string; do
value=${f##*=}
key=${f%%=*}
# if you need environment variable -> eval "qs_$key=$value"
done