Bash: Check if a string contain specific alphabets and comma - string

I am trying to parse and validate a string in Bash which is comma separated. The expected input is: X4,Y1,Z5
Conditions: The string should have only X,Y or Z alphabets, followed by any number. The string should not have any special characters other than comma. Please suggest.
X4,Y1,Z5 (This is OK)
Z2,y6,X1 (This is OK)
X3Y6,Z8 (This is not OK)
A1,B2,X8 (This is not OK)
N1P8* (This is not OK)
I have tried the following but this is not working as expected.
if [[ ! $str =~ ['!##$%^&*()_+'] ]] && [[ $str =~ [XYZxyz] ]]; then
echo "OK"
else
echo "Not OK"
fi

I suppose there are additional conditions of the problem that were implied but not emphasized, such as:
The numbers may have more then one digit.
Each of X,Y,Z letters should be used exactly once.
With that in mind, I think this code will do:
if [[ "$1" =~ ^[XxYyZz][0-9]+(,[XxYyZz][0-9]+){2}$ ]] &&
[[ "$1" =~ .*[Xx].* ]] &&
[[ "$1" =~ .*[Yy].* ]] &&
[[ "$1" =~ .*[Zz].* ]]
then
echo OK
else
echo Not OK
fi
Test cases:
#!/usr/bin/env bash
check() {
[[ "$1" =~ ^[XxYyZz][0-9]+(,[XxYyZz][0-9]+){2}$ ]] &&
[[ "$1" =~ .*[Xx].* ]] &&
[[ "$1" =~ .*[Yy].* ]] &&
[[ "$1" =~ .*[Zz].* ]]
}
test_check() {
# code - expected exit code
# value - string to test
while read code value; do
check "$value"
if [[ $? == $code ]]; then
echo -e "\e[1;32mPassed:\e[0m $value"
else
echo -e "\e[1;31mFailed:\e[0m $value"
fi
done
}
test_check <<EOF
0 x1,y2,z3
0 X1,Y2,Z3
1 x,y,z
1 1,2,3
1 1x,2y,3z
0 z1,x2,y3
1 a1,b2,c3
1 x1
1 x1,y2 z1
1 x1,x2
1 x1;y2;z3
1 x1,y2
1 x1,y2,y3
0 x100,Y500,z0
0 x011,y015,z0
1 x1,x2,y3,z4
1 x1,y1,z1 .
EOF
P.S.
If any of the X,Y,Z may appear in the string more than once or not appear at all, then [[ "$str" =~ ^[XxYyZz][0-9]+(,[XxYyZz][0-9]+)*$ ]] should work. I added here + for digits to appear one or more times after the letter, and quoted "$str" in case if there's a space in it (or, to be precise, any character from $IFS variable).

Related

Bash Password validator

I have stuck with my bash code. and needed some help.
I trying to write bash code that :
Length – minimum of 10 characters.
Contain both alphabet and number.
Include both the small and capital case letters.
Color the output green if it passed the validation and red if it didn’t.
Return exit code 0 if it passed the validation and exit code 1 if it didn’t.,
the problem that can be resolved for me is that code does not check upper and lower case.
and have exit 0, which means the password ok in all cases.
# Color for Output
Warning='\033[0;31m' # Red
Success='\033[0;32m' # Green
NC="\033[0m" # No Color
password=$1
len="${#password}"
if test $len -ge 4 ; then
echo "$password" | grep -eq [[:digit:]]
if test $? -eq 1 ; then
echo "$password" | grep -e [[:upper:]]
if test $? -e 1 ; then
echo "$password" | grep -e [[:lower:]]
if test $? -e 1 ; then
printf "${Success}Strong password!$1\n"
else
echo "weak password include lower case char"
fi
else
echo "weak password include capital char"
fi
else
echo && echo $?
printf "${Success}Strong password!$1\n"
fi
else
echo $?
printf ${Warning}"Try again... \nPassword must have at least 10 characters.$1\n"
fi
In bash, within the [[ ... ]] conditional, the == and != operators do pattern matching. And bash patterns can include POSIX character classes.
error() {
echo "$*" >&2
exit 1
}
password=$1
if (( ${#password} < 10 )); then
error "Too short"
elif [[ $password != *[[:digit:]]* ]]; then
error "Does not contain a digit"
elif [[ $password != *[[:lower:]]* ]]; then
error "Does not contain a lower case letter"
elif [[ $password != *[[:upper:]]* ]]; then
error "Does not contain an upper case letter"
fi
echo "OK"
exit 0
The list of POSIX character classes is documented in the manual at 3.5.8.1 Pattern Matching
That cascading-if can be written more concisely:
(( ${#password} >= 10 )) || error "Too short"
[[ $password == *[[:digit:]]* ]] || error "Does not contain a digit"
[[ $password == *[[:lower:]]* ]] || error "Does not contain a lower case letter"
[[ $password == *[[:upper:]]* ]] || error "Does not contain an upper case letter"
echo OK
[[ ... ]] also has the =~ regular-expression matching operator (there is no !~ negated matching operator):
# it contains a digit, or error
[[ $password =~ [[:digit:]] ]] || error "Does not contain a digit"

Shell script if condition not evaluated for a help info display for the user

I had written a help menu for reference about the usage of a shell script my_script.sh
echo $'\n\n'
echo $(printf '=%.0s' {1..100})
printf ' %.0s' {1..40}
echo "Welcome"
echo $(printf '=%.0s' {1..100})
echo $'\n'
arg=$1
echo "Input : $arg"
echo
if [[ arg -eq "-h" ]] || [[ arg -eq "-H" ]] || [[ arg -eq "-help" ]] || [[ arg -eq "-Help" ]] || [[ arg -eq "--h" ]] || [[ arg -eq "--H" ]] || [[ arg -eq "--help" ]] || [[ arg -eq "--Help" ]]; then
echo "Help menu requested...."
echo $'\n\n'
echo $(printf '~%.0s' {1..100})
printf ' %.0s' {1..43}
echo "Help Menu"
echo $(printf '~%.0s' {1..100})
echo $'\n'
exit 0
else
echo "Executing a program...."
./another_script.sh
fi
When I execute `myscript.sh -h' (or any of the '-' prefixed option), it goes to the if condition, but any other argument doesn't. What am I doing wrong here? I'm new to bash scripts.
Two simple problems with your if:
-eq is for integer comparison, = or == for strings
Use $arg in your if (instead of arg).
But: I would recommend using getopts instead of string comparison. This would make the part more robust, taking care of different ordering of parameters, or when one letter parameters are combined into a single argument.
Unfortunately I do not know the exact reason why your code does not work, but I can offer you a quick fix: You can write "==" instead of "-eq" and prefix your variable "arg" with a dollar sign. Then your script should work fine.
Working example (GNU bash 4.4.19):
arg=$1
if [[ $arg == "-h" ]] || [[ $arg == "-H" ]]; then
echo "Help!"
else
echo "Stop!"
fi
the -eq operation is only used for comparing numbers.
To compare strings uses the operation =
You forgot $ sign for variables arg in Bash, should be $arg
When we use variables in Bash, we should better use double quote.
use #() for multiple strings comparison.
so the if conditions [[ arg -eq "-h" ]] should be [[ "$arg" = "-h" ]]
When comparing a variable with multiple strings, we can use [[ "$arg" = #(-h|-H|--help|--HELP|--h|--H|-help|--HELP) ]].
if [[ "$arg" = #(-h|-H|--help|--HELP|--h|--H|-help|--HELP) ]]; then
echo "Help menu requested...."
echo $'\n\n'
echo $(printf '~%.0s' {1..100})
printf ' %.0s' {1..43}
echo "Help Menu"
echo $(printf '~%.0s' {1..100})
echo $'\n'
exit 0
else
echo "Executing a program...."
./another_script.sh
fi
In addition, we can use boxes(boxes - Command line ASCII boxes unlimited!
) to generate a comment box
cat <<EOF | boxes -a c -d shell -p a5 -s 30x9
HELP MENU
bla bla
EOF
output:
########################################
# #
# #
# HELP MENU #
# #
# bla bla #
# #
# #
########################################

How do I test if a variable is a string in bash?

I tried the following but without success
[root#OBAMA~]# bash
[root#OBAMA~]# a=HelloWorld
[root#OBAMA~]# [[ $a == [A-Za-z] ]] && echo "YES ITS STRING"
( the command not prints anything )
[root#OBAMA~]# [[ $a == [A-Z][a-z] ]] && echo "YES ITS STRING"
( the command not prints anything )
Change your command lke below.
$ [[ $a =~ [A-Za-z]+ ]] && echo "YES ITS STRING"
YES ITS STRING
Use =~ operator to test an input string against a regex.
Add + next to the character class, so that it would repeat the previous pattern or token one or more times. Here it's unnecessary.
Add anchors , in-order to do an exact string match. [[ $a =~ [A-Za-z] ]] && echo "YES ITS STRING" alone will print the string YES ITS STRING because the variable a contains atleast an alphabet.
$ a="HelloWorld"
$ [[ $a =~ ^[A-Za-z]+$ ]] && echo "YES ITS STRING"
YES ITS STRING
$ a="Hello World"
$ [[ $a =~ ^[A-Za-z]+$ ]] && echo "YES ITS STRING"
$
how do you define "a string"
[[ -n $a ]] && echo variable a is not empty
[[ $a == *[[:alpha:]]* ]] && echo variable a contains a letter
shopt -s extglob failglob
[[ $a == +([[:alpha:]]) ]] && echo variable a only has letters
Your glob expressions are not matching because your checking that your variable contains only 1 character or 2 characters.

KSH: search string for multiple substrings

I have a simple way to search for multiple substrings in a single string:
if [[ $string = *"string 1"* && $string = *"string 2"* && $string = *"string 3"* ]]
(here searching for string 1, string 2 and string 3 in string).
How can I simplify this, so that there is only one check?
I've tried:
if [[ $string = *"string 1"*"string 2"*"string 3"* ]]
and
if [[ $string = *"string 1*string 2*string 3"* ]]
Note: the three strings specified here will always be in this order, hence why I can simplify it.
In ksh93, you can use the & sub-pattern delimiter.
$ [[ abcdefg == #(*bcd*&*cde*&*efg*) ]]; echo $?
0
$ [[ abcdefg == #(*bcdz*&*cde*&*efg*) ]]; echo $?
1
Only ksh93 has this unfortunately. In mksh, zsh, and bash, with extended matching, the negation sub-pattern allows for this DeMorgan-like equivalence.
$ [[ abcdefg == !(!(*bcd*)|!(*cde*)|!(*efg*)) ]]; echo $?
0
$ [[ abcdefg == !(!(*bcdz*)|!(*cde*)|!(*efg*)) ]]; echo $?
1
To test for just one pattern, see this FAQ

Combine Unix Test Commands && + ||

KSH - HP-SOL-Lin
Needs to be POSIX
I want to test if multiple conditions are true, then do something.
Assumptions:
FIELDS=0
$STRING='' or $STRING=' '
Right now I have:
if [[ $FIELDS -ne 0 && ! -z $STRING ]]; do
This works fine, if $STRING is null, but sometimes it has a single space. How can I make process the two $STRING tests first? Conceptually, I would like equivalent of:
[[ $FIELDS -ne 0 && [ ! -z $STRING || $STRING = ' ' ] ]]
You can separate the tests:
if [[ $FIELDS -ne 0 ]]; then
if [[ ! -z $STRING || $STRING == ' ' ]]; then
do some stuff
fi
fi
For POSIX conformance,
if [ "$FIELDS" -ne 0 -a ( ! -z "$STRING" -o "$STRING" = " " ) ]; then
...
fi
Bearing in mind that $STRING can be a single space, I'm assuming that the value it is set to (between null and space) is irrelevant, in which case an easier way would be to run a simple check on $STRING first:
if [[ $STRING == ' ' ]]
then
export STRING=''
fi
if [[ $FIELDS -ne 0 && ! -z $STRING ]]; do
Improves readability and means you won't have to execute the same additional tests for subsequent commands.
If my assumptions are incorrect please don't hesitate to send me to the naughty step.

Resources