Comparing SHA-512 Salted Hashes, displays filename during execution - bash

Long story short during execution, i noticed that the "*" in the LIST somehow displays the name of the file i was executing. Is there anyway to let the code display "*" instead of displaying the filename ?
I am still a script newbie, could not think of any way. Please help ...
#!/bin/bash
LIST='W 2 v * %'
encr="Cd9AjUI4nGglIcP3MByrZUnu.hHBJc7.eR0o/v0A1gu0/6ztFfBxeJgKTzpgoCLptJS2NnliZLZjO40LUseED/"
salt="8899Uidd"
for i in $LIST
do
for j in $LIST
do
for k in $LIST
do
for l in $LIST
do
for a in $LIST
do
echo -n "$i$j$k$l$a "
test=`mkpasswd -m SHA-512 $i$j$k$l$a -s $salt`
if [ $test == $encr ] ; then
echo " Password is: $i$j$k$l$a"
exit
fi
done
done
done
done
done
#error displaying *

The same as echo * expands the globulation * to the filenames in the current directory, the same way a=*; for i in $a; do echo $i; done will print paths in the current directory. You can read more about quotes and escaping at various places in the net.
You can use bash arrays to correctly quotes elements:
LIST=(W 2 v "*" %)
for i in "${LIST[#]}"; do
Notes:
Don't use backticks `, they are discouraged. Use $(..) instead.
Quote your variables expansions [ "$test" = "$encr" ] to protect from common bugs like this.
The == is a bash extension, use = to be portable.
The method for generating permutations of elements looks strange and is not scalable. Consider writing a function or using other method. Even (ab-)using bash extension brace expansion echo {W,2,v,"*",%}{W,2,v,"*",%}{W,2,v,"*",%}{W,2,v,"*",%}{W,2,v,"*",%} looks shorter.

Related

bash function name with dash is bad pratice? [duplicate]

What are the syntax rules for identifiers, especially function and variable names, in Bash?
I wrote a Bash script and tested it on various versions of Bash on Ubuntu, Debian, Red Hat 5 and 6, and even an old Solaris 8 box. The script ran well, so it shipped.
Yet when a user tried it on SUSE machines, it gave a "not a valid identifier" error. Fortunately, my guess that there was an invalid character in the function name was right. The hyphens were messing it up.
The fact that a script that was at least somewhat tested would have completely different behaviour on another Bash or distro was disconcerting. How can I avoid this?
From the manual:
Shell Function Definitions
...
name () compound-command [redirection]
function name [()] compound-command [redirection]
name is defined elsewhere:
name A word consisting only of alphanumeric characters and under‐
scores, and beginning with an alphabetic character or an under‐
score. Also referred to as an identifier.
So hyphens are not valid. And yet, on my system, they do work...
$ bash --version
GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)
The question was about "the rules", which has been answered two different ways, each correct in some sense, depending on what you want to call "the rules". Just to flesh out #rici's point that you can shove about any character in a function name, I wrote a small bash script to try to check every possible (0-255) character as a function name, as well as as the second character of a function name:
#!/bin/bash
ASCII=( nul soh stx etx eot enq ack bel bs tab nl vt np cr so si dle \
dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us sp )
for((i=33; i < 127; ++i)); do
printf -v Hex "%x" $i
printf -v Chr "\x$Hex"
ASCII[$i]="$Chr"
done
ASCII[127]=del
for((i=128; i < 256; ++i)); do
ASCII[$i]=$(printf "0X%x" $i)
done
# ASCII table is now defined
function Test(){
Illegal=""
for((i=1; i <= 255; ++i)); do
Name="$(printf \\$(printf '%03o' $i))"
eval "function $1$Name(){ return 0; }; $1$Name ;" 2>/dev/null
if [[ $? -ne 0 ]]; then
Illegal+=" ${ASCII[$i]}"
# echo Illegal: "${ASCII[$i]}"
fi
done
printf "Illegal: %s\n" "$Illegal"
}
echo "$BASH_VERSION"
Test
Test "x"
# can we really do funky crap like this?
function [}{(){
echo "Let me take you to, funkytown!"
}
[}{ # why yes, we can!
# though editor auto-indent modes may punish us
I actually skip NUL (0x00), as that's the one character bash may object to finding in the input stream. The output from this script was:
4.4.0(1)-release
Illegal: soh tab nl sp ! " # $ % & ' ( ) * 0 1 2 3 4 5 6 7 8 9 ; < > \ ` { | } ~ del
Illegal: soh " $ & ' ( ) ; < > [ \ ` | del
Let me take you to, funkytown!
Note that bash happily lets me name my function "[}{". Probably my code is not quite rigorous enough to provide the exact rules for legality-in-practice, but it should give a flavor of what manner of abuse is possible.
I wish I could mark this answer "For mature audiences only."
Command identifiers and variable names have different syntaxes. A variable name is restricted to alphanumeric characters and underscore, not starting with a digit. A command name, on the other hand, can be just about anything which doesn't contain bash metacharacters (and even then, they can be quoted).
In bash, function names can be command names, as long as they would be parsed as a WORD without quotes. (Except that, for some reason, they cannot be integers.) However, that is a bash extension. If the target machine is using some other shell (such as dash), it might not work, since the Posix standard shell grammar only allows "NAME" in the function definition form (and also prohibits the use of reserved words).
From 3.3 Shell Functions:
Shell functions are a way to group commands for later execution using a single name for the group. They are executed just like a "regular" command. When the name of a shell function is used as a simple command name, the list of commands associated with that function name is executed. Shell functions are executed in the current shell context; no new process is created to interpret them.
Functions are declared using this syntax:
name () compound-command [ redirections ]
or
function name [()] compound-command [ redirections ]
and from 2 Definitions:
name
A word consisting solely of letters, numbers, and underscores, and beginning with a letter or underscore. Names are used as shell variable and function names. Also referred to as an identifier.
Note The biggest correction here is that newline is never allowed in a function name.
My answer:
Bash --posix: [a-zA-Z_][0-9a-zA-Z_]*
Bash 3.0-4.4: [^#%0-9\0\1\9\10 "$&'();<>\`|\x7f][^\0\1\9\10 "$&'();<>\`|\x7f]*
Bash 5.0: [^#%0-9\0\9\10 "$&'();<>\`|][^\0\9\10 "$&'();<>\`|]*
\1 and \x7f works now
Bash 5.1: [^#%\0\9\10 "$&'();<>\`|][^\0\9\10 "$&'();<>\`|]*
Numbers can come first?! Yep!
Any bash 3-5: [^#%0-9\0\1\9\10 "$&'();<>\`|\x7f][^\0\1\9\10 "$&'();<>\`|\x7f]*
Same as 3.0-4.4
My suggestion (opinion): [^#%0-9\0-\f "$&'();<>\`|\x7f-\xff][^\0-\f "$&'();<>\`|\x7f-\xff]
Positive version: [!*+,-./:=?#A-Z\[\]^_a-z{}~][#%0-9!*+,-./:=?#A-Z\[\]^_a-z{}~]*
My version of the test:
for ((x=1; x<256; x++)); do
hex="$(printf "%02x" $x)"
name="$(printf \\x${hex})"
if [ "${x}" = "10" ]; then
name=$'\n'
fi
if [ "$(echo -n "${name}" | xxd | awk '{print $2}')" != "${hex}" ]; then
echo "$x failed first sanity check"
fi
(
eval "function ${name}(){ echo ${x};}" &>/dev/null
if test "$("${name}" 2>/dev/null)" != "${x}"; then
eval "function ok${name}doe(){ echo ${x};}" &>/dev/null
if test "$(type -t okdoe 2>/dev/null)" = "function"; then
echo "${x} failed second sanity test"
fi
if test "$("ok${name}doe" 2>/dev/null)" != "${x}"; then
echo "${x}(${name}) never works"
else
echo "${x}(${name}) cannot be first"
fi
else
# Just assume everything over 128 is hard, unless this says otherwise
if test "${x}" -gt 127; then
if declare -pF | grep -q "declare -f \x${hex}"; then
echo "${x} works, but is actually not difficult"
declare -pF | grep "declare -f \x${hex}" | xxd
fi
elif ! declare -pF | grep -q "declare -f \x${hex}"; then
echo "${x} works, but is difficult in bash"
fi
fi
)
done
Some additional notes:
Characters 1-31 are less than ideal, as they are more difficult to type.
Characters 128-255 are even less ideal in bash (except on bash 3.2 on macOS. It might be compiled differently?) because commands like declare -pF do not render the special characters, even though they are there in memory. This means any introspection code will incorrectly assume that these functions are not there. However, features like compgen still correctly render the characters.
Out of my testing scope, but some unicode does work too, although it's extra hard to paste/type on macOS over ssh.
This script tests all valid chars for
function names with 1 char.
It outputs 53 valid chars (a-zA-Z and underscore) using
a POSIX shell and 220 valid chars with BASH v4.4.12.
The Answer from Ron Burk is valid, but lacks the numbers.
#!/bin/sh
FILE='/tmp/FOO'
I=0
VALID=0
while [ $I -lt 256 ]; do {
NAME="$( printf \\$( printf '%03o' $I ))"
I=$(( I + 1 ))
>"$FILE"
( eval "$NAME(){ rm $FILE;}; $NAME" 2>/dev/null )
if [ -f "$FILE" ]; then
rm "$FILE"
else
VALID=$(( VALID + 1 ))
echo "$VALID/256 - OK: $NAME"
fi
} done

How to disable special characters in bash? [duplicate]

This question already has an answer here:
Prevent globbing in a bash script
(1 answer)
Closed 6 years ago.
I've created a script which performs a calculation. For example:
count 1 + 3
1 + 3 = 4,
- and / also works, but if I type
count 1 * 3, I got
Should be number operand number
Here is a part of a script:
if [ "$#" -ne 3 ]; then
echo "Should be number operand number"
exit 1
fi
...
elif [ "$2" = "*" ]; then
result=`echo $1 \* $3 | bc`
echo "$1 * $3 = $result"
Yes, if I type \ before * in command line it would work, but I need it to run just with a *.
I've tried to use set -f inside the script it did not work (yes it disables special characters if I type in bash itself but it is not what I need). There is also a shopt command that controls shell behavior, so I've tried to enable some options like shopt -s globstar, nullglob, promptvars, etc. It did not work. I put the command before if statement maybe it is wrong.
I would appreciate if someone corrects me or tells the other way to disable interpretation of a special character from inside the script.
This exact problem is mentioned in the POSIX specification of expr, saying that the tool has a "rather difficult syntax" for the reason you describe.
This is definitely a known problem, in other words, and POSIX itself leaves it unsolved. You should really consider working with Unix and do the same, rather than trying to work against it.
Some options are:
Require the entire expression to one quoted argument, e.g. count "1 * 2"
This is a pragmatic, fail-fast way of ensuring that the argument is always quoted, so that any introduction of * still works. The bash builtin let does this.
read the expression yourself, e.g. just run count and then type in 1 * 2.
This avoids the shell touching your data, so that you can interpret it however you want. It's what bc and dc do.
Live with the fact that passing * will require careful quoting.
This is what expr does.
Use a magic alias hack that only works on certain shells under very specific circumstances.
This is what no one does, because it's fragile and unpredictable, even though it in some cases allows the syntax you want.
Always quote when you echo. You need to quote the variable reference as well:
me$ FOO="BAR * BAR"
me$ echo "$FOO"
BAR * BAR
If you don't use double quote, bash will see * and perform filename expansion. * expands to all files in your current directory.
However in saying this you would have to be using single quotes for the *:
#!/bin/bash
if [ "$2" = "*" ]; then
result=`echo $1 \* $3 | bc`
echo "$1 * $3 = $result"
fi
The following would work:
» count.sh 2 '*' 1
2 * 2 = 4
There is nothing you can do in your program, because the * expansion is done by the shell (in contrast to Windows, where it's done by the program).
Here's another code example using a menu-driven approach instead:
#!/bin/bash
while true; do
read -p "what's the first number? " n1
read -p "what's the second number? " n2
PS3="what's the operation? "
select ans in add subtract multiply divide; do
case $ans in
add) op='+' ; break ;;
subtract) op='-' ; break ;;
multiply) op='*' ; break ;;
divide) op='/' ; break ;;
*) echo "invalid response" ;;
esac
done
ans=$(echo "$n1 $op $n2" | bc -l)
printf "%s %s %s = %s\n\n" "$n1" "$op" "$n2" "$ans"
done
what's the first number? 5
what's the second number? 4
1) add
2) subtract
3) multiply
4) divide
what's the operation? /
invalid response
what's the operation? 4
5 / 4 = 1.25000000000000000000
In short, quote everything where you do not require the shell to perform token splitting and wildcard expansion.

Variable substitution in a for-loop using {$var}

I'm very new to bash scripting and I'm trying to practice by making this little script that simply asks for a range of numbers. I would enter ex. 5..20 and it should print the range, however - it just echo's back whatever I enter ("5..20" in this example) and does not expand the variable. Can someone tell me what I'm doing wrong?
Script:
echo -n "Enter range of number to display using 0..10 format: "
read range
function func_printrage
{
for n in {$range}; do
echo $n
done
}
func_printrange
Brace expansion in bash does not expand parameters (unlike zsh)
You can get around this through the use of eval and command substitution $()
eval is evil because you need to sanitize your input otherwise people can enter ranges like rm -rf /; and eval will run that
Don't use the function keyword, it is not POSIX and has been deprecated
use read's -p flag instead of echo
However, for learning purposes, this is how you would do it:
read -p "Enter range of number to display using 0..10 format: " range
func_printrange()
{
for n in $(eval echo {$range}); do
echo $n
done
}
func_printrange
Note: In this case the use of eval is OK because you are only echo'ing the range
One way is to use eval,
crude example,
for i in $(eval echo {0..$range}); do echo $i; done
the other way is to use bash's C style for loop
for((i=1;i<=20;i++))
do
...
done
And the last one is more faster than first (for example if you have $range > 1 000 000)
One way to get around the lack of expansion, and skip the issues with eval is to use command substitution and seq.
Reworked function (also avoids globals):
function func_print_range
{
for n in $(seq $1 $2); do
echo $n
done
}
func_print_range $start $end
Use ${} for variable expansion. In your case, it would be ${range}. You left off the $ in ${}, which is used for variable expansion and substitution.

How to parse $QUERY_STRING from a bash CGI script?

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

how to grep a variable in the shell program? [duplicate]

This question already has answers here:
grep for expression containing variable
(2 answers)
Closed 9 years ago.
#!/bin/bash
for ((var=0; var<20; var++))
do
echo " Number is: $(grep 'Multiple_Frame = echo **$var**' 20mrf.txt | wc -l)" >>statisic.txt
done
This shell program cannot produce correct result which maybe the reason of wrong variable returning in the second grep command.
How can I grep a variable within the second echo sentence? to grep different things according to the var changing?
Many thanks!
As others have stated, the problem is that the single quotes prevent expansion of the variable. However, using $() allows you to use double quotes:
echo " Number is: $(grep "Multiple_Frame = echo **$var**" 20mrf.txt | wc -l)" >>statisic.txt
although I suspect something like this is what you meant:
echo " Number is: $(grep "Multiple_Frame = $var" 20mrf.txt | wc -l)" >>statisic.txt
You should also be aware that grep has an option to output the count so you can omit wc:
echo " Number is: $(grep -c "Multiple_Frame = $var" 20mrf.txt)" >>statisic.txt
#OP, doing what you do that way is rather inefficient. You are calling grep and wc 20 times on the same file. Open the file just once, and get all the things you want in 1 iteration of the file contents.
Example in bash 4.0
declare -A arr
while read -r line
do
case "$line" in
*"Multiple_Frame ="*)
line=${line#*Multiple_Frame = }
num=${line%% *}
if [ -z ${number_num[$num]} ] ;then
number_num[$num]=1
else
number_num[$num]=$(( number_num[$num]+1 ))
fi
;;
esac
done <"file"
for i in "${!number_num[#]}"
do
echo "Multiple_Frame = $i has ${number_num[$i]} counts"
done
similarly, you can use associative arrays in gawk to help you do this task.
gawk '/Multiple_Frame =/{
sub(/.*Multiple_Frame = /,"")
sub(/ .*/,"")
arr["Multiple_Frame = "$0]=arr["Multiple_Frame = "$0]+1
}END{
for(i in arr) print i,arr[i]
}' file
You have to store each substitution in a variable. Like this:
#!/bin/bash
for ((var=0; var < 20; var++))
do
count=`grep "Multiple_Frame = $var" 20mrf.txt | wc -l`
echo " Number is: $count" >> statisic.txt
done
Ok, the second [sic] problem is with your quoting on line 5. The reference to $var will never be expanded because it's contained within single quotes. You can fix that by replacing the single quotes (') with escaped double quotes (\").
The first [sic] problem is that you're trying to do too much in a single line, which causes your nesting problem with quotes. Break the line up into multiple commands, storing intermediary results as necessary. Yeah, it might run a tad slower, but you'll save a LOT of time debugging and maintaining it.
Trader's Second Law: If you have to choose between optimizing for performance, and optimizing for maintainability, ALWAYS choose to make your code more maintainable. Computers get faster all the time; Programmers don't.
The string inside $(...) is quoted with single quotes ('...') this quoting prevents the variable expansion. Use Double quotes instead. In your case:
#!/bin/bash
for ((var=0; var<20; var++))
do
echo " Number is: $(grep 'Multiple_Frame = echo **'"$var"'**' 20mrf.txt | wc -l)" >>statisic.txt
done
Note that in this example, it seems like the double quotes for the echo command are already opened, but you should note that the $(...) is evaluated first, and there is no double quotes inside it. So the change here is to close the single quote of the grep open double quote instead, and close the double quotes and reopen single quote later.
This lengthy explanation illustrates the benefit of breaking the expression apart, as suggested by other answers.

Resources