Here is what I'm trying to do in one of my bash scripts.
If SERVER_ENV is not PROD or TEST, the script must exit.
check-server-env() {
local SERVER_ENV="$1"
if ! [[ "$SERVER_ENV"^^ =~ ^(TEST|PROD)$ ]]; then
error "$(highlight "$SERVER_ENV")" " does not exist."
echo "Exiting script..."
echo ""
exit 0
fi
}
I call script.sh TEST
SERVER_ENV=$1
check-server-env $SERVER_ENV
Here is how I'm calling. And it's not working. What am I doing wrong?
You may use:
check-server-env() {
local SERVER_ENV="$1"
if [[ ! "${SERVER_ENV^^}" =~ ^(TEST|PROD)$ ]]; then
error "$(highlight "$SERVER_ENV")" " does not exist."
echo "Exiting script..."
echo ""
exit 0
fi
}
However you may ditch regex and use extglob matching in bash:
check-server-env() {
local SERVER_ENV="$1"
if [[ "${SERVER_ENV^^}" != #(TEST|PROD) ]]; then
error "$(highlight "$SERVER_ENV")" " does not exist."
echo "Exiting script..."
echo ""
exit 0
fi
}
Why use regex at all?
if [ "${SERVER_ENV^^}" != "PROD" ] && [ "${SERVER_ENV^^}" != "TEST" ]; then
error "$(highlight "$SERVER_ENV")" " does not exist."
echo "Exiting script..."
echo ""
exit 0
fi
Related
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
Here I found nice solution to test if variable is a number:
case $string in
''|*[!0-9]*) echo bad ;;
*) echo good ;;
esac
I'm trying to modify it to test if variable is a 1,2,3 or 4-digit natural number (within a range 0-9999), so I added |*[0-9]{5,}* to exclude numbers with more than 5 digits, but the code below prints good for numbers greater then 9999 e.g. 1234567.
case $string in
''|*[!0-9]*|*[0-9]{5,}*) echo bad ;;
*) echo good ;;
esac
I'm using ash from busybox.
You can use numeric testing:
$ s=123
$ (( s>=0 && s<=9999 )) && echo "true" || echo "false"
true
$ s=123456
$ (( s>=0 && s<=9999 )) && echo "true" || echo "false"
false
$ s=-1
$ (( s>=0 && s<=9999 )) && echo "true" || echo "false"
false
You just need to make sure that the string is all digits with optional ± at the start. A non numeric string used in numeric context will evaluate to 0 so you need to test for that.
Which you can use your case statement for:
case $s in
''|*[!0-9]*) echo bad ;;
*) (( s>=0 && s<=9999 )) && echo "true" || echo "false" ;;
esac
Or:
$ [ "$s" -eq "$s" ] 2>/dev/null && (( s>=0 && s<=9999 )) && echo "true" || echo "false"
Works too.
These should work under any POSIX shell.
Not sure if you need this in a case statement, since I would just do:
if { test "$string" -ge 0 && test "$string" -lt 10000; } 2> /dev/null; then
echo good
else
echo bad
fi
If you want to use a case statement in a strictly portable shell, you're probably stuck with:
case $string in
[0-9]|[0-9][0-9]|[0-9][0-9][0-9]|[0-9][0-9][0-9][0-9]) echo good;;
*) echo bad;;
esac
case $([[ $num =~ ^[0-9]+$ ]] && echo 0) in
0) echo good ;;
*) echo bad ;;
esac
I am writing a script in unix, which will take options as parameter as shown:
./command -pers
The options allowed are p, e, r, s and can be in any order and are optional also. Example, these are correct syntax: ./command -e, ./command -sr, ./command -pes, but this one is incorrect ./command -pep, ./command -ssr. Repetition of option is not allowed, but atleast one option is required.
For the same I have used regular expression, but it is not avoiding repetition.
But it is allowing repetition. Please tell what is wring with the expression.
[[ $1 =~ ^-[p{0,1}r{0,1}s{0,1}e{0,1}]{1,4}$ ]] || { echo "$MSG_INCORRECT_USAGE"; }
You can use a script opt.sh like this to avoid processing each passed option more than once:
#!/bin/bash
while getopts "pers" opt; do
[[ -n ${!opt} ]] && { echo "Error: $opt already processed"; exit 1; } || declare $opt=1
case $opt in
p) echo "processing p!" ;;
e) echo "processing e!" ;;
r) echo "processing r!" ;;
s) echo "processing s!" ;;
\?) echo "Invalid option: -$OPTARG" ;;
esac
done
Testing:
bash ./opt.sh -srr
processing s!
processing r!
Error: r already processed
bash ./opt.sh -pep
processing p!
processing e!
Error: p already processed
bash ./opt.sh -pers
processing p!
processing e!
processing r!
processing s!
#!/bin/bash
while getopts "pers" OPTION; do
echo $OPTION
done
Results:
$ bash test.sh -pers
p
e
r
s
Replace echo $OPTION with a case statement and report errors if an option appears twice. Example:
#!/bin/bash
unset OPT_P OPT_E OPT_R OPT_S
while getopts "pers" OPTION; do
case $OPTION in
p)
if [ $OPT_P ]; then
echo "-p appeared twice"
exit 64
else
OPT_P="true"
fi
;;
#... and so on ...
\?)
echo "Unrecognized option $OPTION"
exit 64
;;
done
arg0=$(basename $0 .sh)
error() { echo "$arg0: $*" >&2; exit 1; }
usage() { echo "Usage: $arg0 [-pers]" >&2; exit 1; }
p_flag=
e_flag=
r_flag=
s_flag=
while getopts pers arg
do
case "$arg" in
(e) [ -z "$e_flag" ] || error "-e flag repeated"
e_flag=1;;
(p) [ -z "$p_flag" ] || error "-p flag repeated"
p_flag=1;;
(r) [ -z "$r_flag" ] || error "-r flag repeated"
r_flag=1;;
(s) [ -z "$s_flag" ] || error "-s flag repeated"
s_flag=1;;
(*) usage;;
esac
done
shift $(($OPTIND - 1))
[ -z "$e_flag$p_flag$r_flag$s_flag" ] && error "You must specify one of -e, -p, -r, -s"
[ $# = 0 ] || error "You may not specify any non-option arguments"
…process the code for each option that was set…
If you need to process the options in the sequence they're given, then you need to record the order in which they arrive. Use an array:
flags=()
before the loop, and
flags+=("$arg")
after the esac. The processing can then process each element of the $flags in sequence.
for flag in "${flags[#]}"
do
…processing for the particular flag…
done
I wrote simple script to validate IP address and Netmask as follows
#!/bin/bash
validFormatIP()
{
echo $1 | grep -w -E -o '^(25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[1]?[1-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' > /dev/null
if [ $? -eq 0 ]
then
echo "Valid ipaddress"
else
echo "Inalid ipaddress"
fi
}
validNetMask()
{
echo $1 | grep -w -E -o '^(254|252|248|240|224|192|128)\\.0\\.0\\.0|255\\.(254|252|248|240|224|192|128|0)\\.0\\.0|255\\.255\\.(254|252|248|240|224|192|128|0)\\.0|255\\.255\\.255\\.(254|252|248|240|224|192|128|0)' > /dev/null
if [ $? -eq 0 ]
then
echo "Valid netmask"
else
echo "Invalid netmask"
fi
}
setIpAddress()
{
ip_address=`echo $1 | awk -F= '{print $2}'`
validFormatIP $ip_address
}
setNetMask()
{
ip_address=`echo $1 | awk -F= '{print $2}'`
validNetMask $ip_address
}
let "n_count=0"
netmask=""
let "i_count=0"
ipaddess=""
while getopts ":i:n:" OPTION
do
case $OPTION in
n)
netmask="Netmask=$OPTARG"
let "n_count+=1"
;;
i)
ipaddess="IpAddess=$OPTARG"
let "i_count+=1"
;;
?)
echo "wrong command syntax"
;;
esac
done
if [ $i_count -eq 1 ]
then
setIpAddress $ipaddess
exit 0
fi
if [ $n_count -eq 1 ]
then
setNetMask $netmask
exit 0
fi
Using above result i have successfully filter out invalid IPaddress but not able to filter invalid netmask.I have run above script with different argument as follows and see the output also below after script executing
$ ./script.bash -i 192.168.0.1
Valid ipaddress
$./script.bash -i 255.255.0.0
Inalid ipaddress
$./script.bash -n 255.255.255.0
Invalid netmask
As you see above output the result for IP address validation is expected but why it reject the netmask even i enter valid netmask `255.255.255.0 ?
Any one have idea what i miss in netmask validation or something wrong in my script?
grep doesn't double escaping dots etc so this will work:
validNetMask() {
echo $1 | grep -w -E -o '^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)' > /dev/null
if [ $? -eq 0 ]; then
echo "Valid netmask"
else
echo "Invalid netmask"
fi
}
Better to use this concise version:
validNetMask() {
grep -E -q '^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)' <<< "$1" && echo "Valid netmask" || echo "Invalid netmask"
}
mine:
validate_netmask () {
n_masks=(${1//./ })
[ "${#n_masks[#]}" -ne 4 ] && return 1
for i in ${1//./ }; do
bits=$(echo "obase=2;ibase=10;$i" | bc)
pre=$((8-${#bits}))
if [ "$bits" = 0 ]; then
zeros=00000000
elif [ "$pre" -gt 0 ]; then
zeros=$(for ((i=1;i<=$pre;i++)); do echo -n 0; done)
fi
b_mask=$b_mask$zeros$bits
unset zeros
done
if [ $b_mask = ${b_mask%%0*}${b_mask##*1} ]; then
return 0
else
return 1
fi
}
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.