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"
Related
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).
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 #
# #
# #
########################################
I want to do an if statement with three conditions that have to be satisfied at the same time. I am using Ubuntu Bash for Windows and the values $c1, $c2 and $c3 are non-integer (decimal negative numbers).
if [ (( $(echo "$c1 < 0" | bc -l) )) ] && [ (( $(echo "$c2 < 0" | bc -l) )) ] && [ (( $(echo "$c3 < 0" | bc -l) )) ];
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
However, I get the following syntax error in the if line: syntax error near unexpected token `('
If I just put one condition:
if (( $(echo "$c1 < 0" | bc -l) ));
it works, but when I add the three of them as AND (&&), I get the error. Can anyone help me?
Considerably more efficient (assuming you know your values are numbers, and only need to check whether they're all negative) would be:
if [[ $c1 = -* ]] && [[ $c2 = -* ]] && [[ $c3 = -* ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
If you want to be more specific about the permissible formats (f/e, allowing leading spaces), a regex is another option, which similarly can be implemented more efficiently than spawning a series of subshells invoking bc:
nnum_re='^[[:space:]]*-([[:digit:]]*[.][[:digit:]]+|[[:digit:]]+)$'
if [[ $c1 =~ $nnum_re ]] && [[ $c2 =~ $nnum_re ]] && [[ $c3 =~ $nnum_re ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
First, pass the relational AND operators into bc to get rid of some punctuation (also only invokes bc once):
if (( $(echo "$c1 < 0 && $c2 < 0 && $c3 < 0" | bc -l) == 1 ))
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
Although if it were me, I would create a shell function returning a "true" exit status if bc evaluates the result of an expression to non-zero. Then you can hide most of the ugly punctuation in one place separated from your main logic:
function bc_true() {
(( $(echo "$#" | bc -l) != 0 ))
}
And write a (IMO) cleaner shell expression:
if bc_true "$c1 < 0 && $c2 < 0 && $c3 < 0"
then
...
trying to validate a string at command line. each character should be among A-Z, a-z, 0-9 , special char(comma, underscore, period). If there are any other characters, display "invalid" else valid"
eg:
echo "hello123.txt" returns "valid"
echo "hello?.txt" returns "invalid"
echo "HEllo_hello" returns "valid"
Thank you.
If you have a suitable version of grep, you can use grep -v to determine this:
echo "test" | grep -v "^[A-Za-z0-9,_.]*$" > /dev/null
echo $? # 1
echo "#test" | grep -v "^[A-Za-z0-9,_.]*$" > /dev/null
echo $? # 0
In bash, you can use pattern matching on the right hand side of the == operator in [[ ... ]]:
#!/bin/bash
for string in 'hello123.txt' 'hello?.txt' 'HEllo_hello' ; do
if [[ $string == +([A-Za-z0-9,_.]) ]] ; then
echo valid
else
echo invalid
fi
done
You could create a script such as:
#!/bin/bash
if [[ $1 = "" ]] ; then
echo "Please run the following command with a string at the end...\
Example= ./script.bash testing"
exit 2
echo "$1" | grep -qi "^[a-z0-9.,_]*$"
if [[ $? = "0" ]] ; then
echo "Valid"
else
echo "Invalid"
fi
exit 0
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.