What's the error with this BASH regex script? - regex

I'm trying to make a program that reads in n strings and checks them for pertaining to a regex pattern: XXXXX1234X where X is an uppercase character and {1,2,3,4} is any digit. As far as I checked, the regex pattern is correct. The problem seems to be in the input and comparison of strings.
read n
i=0
declare -a str
while [ $i -lt $n ]
do
read 'str[$i]'
i=$((i+1))
done
i=0
while [ $i -lt $n ]
do
[[ $(str[$i]) =~ ^([A-Z]){5}([0-9]){4}([A-Z]){1}$ ]] && echo YES || echo NO
i=$((i+1))
done

I did a minor modification to your code, I replaced the ( and ) with { } in the regex test:
[[ ${str[$i]} =~ ^...
Ran some test and it worked:
#!/bin/bash
read n
i=0
declare -a str
while [ $i -lt $n ]
do
read 'str[$i]'
i=$((i+1))
done
i=0
while [ $i -lt $n ]
do
[[ ${str[$i]} =~ ^([A-Z]){5}([0-9]){4}([A-Z]){1}$ ]] && echo YES || echo NO
i=$((i+1))
done

Related

smallest value issue in shell script

I'm trying to find most negative value that are stored in array.
I used the following code
small=99999.999
for M in ${val[#]}
do
if [ "$M" \< "$small" ]
then small="$M"
fi
done
Let's consider the values are
0
0.5764
1.2934
0.8826
3.3143
2.8783
4.5771
0.549
2.4977
0.2294
1.0407
-0.0854
0.1819
1.911
0.5448
1.1276
0.2128
1.5406
-0.2361
-0.7184
-0.0082
The above code is producing smallest value as -0.0082 which is not the answer.
Can anybody pl let me know the fact?
You are comparing strings (lexicographical order), instead you can put your condition in double parenthesis (to perform a numeric comparison) if((a < b)); then or use -lt notation:
#!/bin/bash
for M in ${val[#]}
do
if [ "$M" -lt "$small" ]
then small="$M"
fi
done
Try it out in bash:
if \[ 100 \< 2 \]; then echo "yes"; else echo "no"; fi
it will output yes, but its false.
if ((100 < 2)); then echo "yes"; else echo "no"; fi
it will output no, which is true.
if \[ 100 -lt 2 \]; then echo "yes"; else echo "no"; fi
it will also give you the correct answer.
But bash only handles integer math, so you can use bc in your condition:
if [ $(bc <<< "$M < $small") -eq 1 ]

Negative to positive number range in regex in bash scripting [duplicate]

I want to just insert number between two values, and otherwise the script repeated until correct number.
This is my script and it does not work correctly:
validation(){
read number
if [ $number -ge 2 && $number -ls 5 ]; then
echo "valid number"
break
else
echo "not valid number, try again"
fi
}
echo "insert number"
validation
echo "your number is" $number
If you are using Bash, you are better off using the arithmetic expression, ((...)) for readability and flexibility:
if ((number >= 2 && number <= 5)); then
# your code
fi
To read in a loop until a valid number is entered:
#!/bin/bash
while :; do
read -p "Enter a number between 2 and 5: " number
[[ $number =~ ^[0-9]+$ ]] || { echo "Enter a valid number"; continue; }
if ((number >= 2 && number <= 5)); then
echo "valid number"
break
else
echo "number out of range, try again"
fi
done
((number >= 2 && number <= 5)) can also be written as ((2 <= number <= 5)).
See also:
Test whether string is a valid integer
How to use double or single brackets, parentheses, curly braces
Your if statement:
if [ $number -ge 2 && $number -ls 5 ]; then
should be:
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
Changes made:
Quoting variables is considered good practice.
ls is not a valid comparison operator, use le.
Separate single-bracket conditional expressions with &&.
Also you need a shebang in the first line of your script: #!/usr/bin/env bash
if [ $number -ge 2 && $number -ls 5 ]; then
should be
if [[ $number -ge 2 && $number -le 5 ]]; then
see help [[ for details
Try bellow code
echo "Enter number"
read input
if [[ $input ]] && [ $input -eq $input 2>/dev/null ]
then
if ((input >= 1 && input <= 4)); then
echo "Access Granted..."
break
else
echo "Wrong code"
fi
else
echo "$input is not an integer or not defined"
fi
2 changes needed.
Suggested by Sergio.
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
There is no need of break. only meaningful in a for, while, or until loop
while :; do
read option
if [[ $option -ge 1 && $option -lt 4 ]]; then
echo "correct"
c
break
else
echo "Incorrect option selected,choose an option between [1-4]"
fi
done

How should I use exact keyword matching as a condition in the case statement?

I was trying to write myself some handy scripts in order to legitimately slacking off work more efficiently, and this question suddenly popped up:
Given a very long string $LONGEST_EVER_STRING and several keywords strings like $A='foo bar' , $B='omg bbq' and $C='stack overflow'
How should I use exact keyword matching as a condition in the case statement?
for word in $LONGEST_EVER_STRING; do
case $word in
any exact match in $A) do something ;;
any exact match in $B) do something ;;
any exact match in $C) do something ;;
*) do something else;;
esac
done
I know I can write in this way but it looks really ugly:
for word in $LONGEST_EVER_STRING; do
if [[ -n $(echo $A | fgrep -w $word) ]]; then
do something;
elif [[ -n $(echo $B | fgrep -w $word) ]]; then
do something;
elif [[ -n $(echo $C | fgrep -w $word) ]]; then
do something;
else
do something else;
fi
done
Does anyone have an elegant solution? Many thanks!
You could use a function to do a little transform in your A, B, C variables and then:
shopt -s extglob
Ax="+(foo|bar)"
Bx="+(omg|bbq)"
Cx="+(stack|overflow)"
for word in $LONGEST_EVER_STRING; do
case $word in
$Ax) do something ;;
$Bx) do something ;;
$Cx) do something ;;
*) do something else;;
esac
done
I would just define a function for this. It'll be slower than grep for large wordlists, but faster than starting up grep many times.
##
# Success if the first arg is one of the later args.
has() {
[[ $1 = $2 ]] || {
[[ $3 ]] && has "$1" "${#:3}"
}
}
$ has a b c && echo t || echo f
f
$ has a b c a d e f && echo t || echo f
t
A variation on /etc/bashrc's "pathmunge"
for word in $LONGEST_EVER_STRING; do
found_word=false
for list in " $A " " $B " " $C "; do
if [[ $list == *" $word "* ]]; then
found_word=true
stuff with $list and $word
break
fi
done
$found_word || stuff when not found
done

Bash Regex for empty string returns true

if [[ " " =~ ^[0-9]*$ ]]; then echo "si"; else echo "no"; fi; //Echoes No
if [[ "" =~ ^[0-9]*$ ]]; then echo "si"; else echo "no"; fi; //Echoes Yes
Is this a bug or am I missing something?
This is as expected. You specified 0 or more times (*) a digit ([0-9]). An empty string is 0 times that.
Use a + (which means "1 or more times") instead of a *:
if [[ " " =~ ^[0-9]+$ ]]; then echo "si"; else echo "no"; fi; // Should echo No
if [[ "" =~ ^[0-9]+$ ]]; then echo "si"; else echo "no"; fi; // Should echo No
The first one is a space, which does not match the [0-9]* regex.
The second is empty, which is [0-9]* because * also implies 0 ocurrencies. If you make it match at least one ocurrency with +, then it is false:
$ if [[ " " =~ ^[0-9]+$ ]]; then echo "si"; else echo "no"; fi;
no
* in a regex means "0 or more", so with nothing in the target string, the regex trivially matches.
[0-9]* matches zero or more digits, so yes, it matches the empty string. If you don't want to match the empty string, use [0-9]+, which matches one or more digits.

How can I check the last character in a string in bash?

I need to ensure that the last character in a string is a /
x="test.com/"
if [[ $x =~ //$/ ]] ; then
x=$x"extention"
else
x=$x"/extention"
fi
at the moment, false always fires.
Like this, for example:
$ x="test.com/"
$ [[ "$x" == */ ]] && echo "yes"
yes
$ x="test.com"
$ [[ "$x" == */ ]] && echo "yes"
$
$ x="test.c/om"
$ [[ "$x" == */ ]] && echo "yes"
$
$ x="test.c/om/"
$ [[ "$x" == */ ]] && echo "yes"
yes
$ x="test.c//om/"
$ [[ "$x" == */ ]] && echo "yes"
yes
You can index strings in Bash using ${var:index} and ${#var} to get the length of the string. Negative indices means the moving from the end to the start of the string so that -1 is index of the last character:
if [[ "${x:${#x}-1}" == "/" ]]; then
# last character of x is /
fi
Your condition was slightly incorrect. When using =~, the rhs is considered a pattern, so you'd say pattern and not /pattern/.
You'd have got expected results if you said
if [[ $x =~ /$ ]] ; then
instead of
if [[ $x =~ //$/ ]] ; then
You can do this generically using bash substrings $(string:offset:length} - length is optional
#x is the length of x
Therefore
$n = 1 # 1 character
last_char = ${x:${#x} - $n}
For future references,
$ man bash
has all the magic
${parameter:offset:length}
Substring Expansion. Expands to up to length characters of parameter
starting at the character specified by offset. If length is
omitted, expands to the substring of parameter starting at the
character specified by offset. length and offset are arithmetic
expressions ...