Bash Shell help regular expressions not working on password check - regex

I am trying to make regular expression work with my password checker script but for some reason it is just not accepting it. What I am trying to do is require the following characters, the minimum of:
two upper case letters, two lower case letters, minimum 2 numbers and minimum two special characters. The special characters I am looking towards are just "!#" and that is it.
Here is the script,
#!/bin/bash
read -s -p "Enter Password: " password
password_length=${#password}
if [ $password_length -lt 8 -o $password_length -gt 20 ] ;then
echo -e "Invalid password - should be between 8 and 20 characters in length.";
echo ;
else
# Check for invalid characters
case $password in
*[^a-zA-Z0-9]* )
echo -e "Password contains invalid characters.";
echo ;
;;
* )
echo "Password accepted.";
echo;;
esac
fi

I don't have a good answer. At the moment I'm playing around with using grep as it has a far more powerful regex engine.
Something like this:
#!/usr/bin/env bash
while true; do
echo "Password must contain 2 upper case characters and two numbers"
read -s -p "Enter Password: " password
echo
FAIL=no
# Mininum 8 characters
[[ ${#password} -ge 8 ]] || FAIL=yes
# Must contain 2 upper case letters
echo $password | grep -q "[A-Z].*[A-Z]" || FAIL=yes
# Must contain 2 digits
echo $password | grep -q "[0-9].*[0-9]" || FAIL=yes
# Must contain one non-alphanumeric character (no spaces)
echo $password | grep -q "[^a-zA-Z0-9 ]" || FAIL=yes
[[ ${FAIL} == "no" ]] && break
echo "Password invalid"
done
echo "Password $password is valid"
Anyway, what I would do is to test the possible password against each requirement separately, as it's easy to get lost in regular expressions, and you might miss some edge cases.

BASH regex engine doesn't support lookarounds as in your regex.
This example is for :
Password should be at least 6 characters long with at least one digit and at least one Upper case Alphabet.
You can use following shell glob check to make sure password matches your criteria:
[[ ${#s} -ge 6 && "$s" == [A-Z] && "$s" == [a-z] && "$s" == [0-9] ]]
ref : Regex password validation in Bash shell

Related

Bash regex match spanning multiple lines

I'm trying to create a bash script that validates files. One of the requirements is that there has to be exactly one "2" in the file.
Here's my code at the moment:
regex1="[0-9b]*2[0-9b]*2[0-9b]*"
# This regex will match if there are at least two 2's in the file
if [[ ( $(cat "$file") =~ $regex1 ) ]]; then
# stuff to do when there's more than 1 "2"
fi
#...
regex2="^[013456789b]*$"
# This regex will match if there are at least no 2's in the file
if [[ ( $(cat "$file") =~ $regex2 ) ]]; then
# stuff to do when there are no 2's
fi
What I'm trying to do is match the following pieces:
654654654654
254654845845
845462888888
(because there are 2 2's in there, it should be matched)
987886546548
546546546848
654684546548
(because there are no 2's in there, it should be matched)
Any idea how I make it search all lines with the =~ operator?
I'm trying to create a bash script that validates files. One of the
requirements is that there has to be exactly one "2" in the file.
Try using grep
#!/bin/bash
file='input.txt'
n=$(grep -o '2' "$file" | wc -l)
# echo $n
if [[ $n -eq 1 ]]; then
echo 'Valid'
else
echo 'Invalid'
fi
How about this:
twocount=$(tr -dc '2' input.txt | wc -c)
if (( twocount != 1 ))
then
# there was either no 2, or more than one 2
else
# exactly one 2
fi
Using anchors as you've been, match a string of non-2s, a 2, and another string of non-2s.
^[^2]*2[^2]*$
Multiline regex match is indeed possible using awk with null record separator.
Consider below code:
awk '$0 ~ /^.*2.*2/ || $0 ~ /^[013456789]*$/' RS= file
654654654654
254654845845
845462888888
Take note of RS= which makes awk join multiple lines into single line $0 until it hits a double newline.

regular expressions in tcsh shell script

I have a shell script in tcsh to which I pass an argument, the length of which can vary. The possible values of the argument are the letters -c,s,i,q,a. and also a combination of these letters. (e.g: cs,si,ca,iq,qa,csq,acs,csia ..etc). The order of the letters does not matter.
My problem is to check the argument for any character other than these 5 and if any of the valid character appear more than one time (zero time is ok). The script should check both the conditions and throw an error. Is there any regular expression that I can use with if statement for this purpose?
Here is a sample piece of code you can use. The use of an "X" is to force the comparison to be a string.
#!/bin/csh -f
if ( $#argv > 0 ) then
echo arg is $1
if ( X$1 =~ X-* ) then
echo "we have an argument"
if ( "X$1" =~ X-c[aeiou] ) then
echo I found -c followed by vowel
else if ( "X$1" =~ "X-c" ) then
echo I found -c alone
else
echo I found a -c but not a valid combo
endif
else
echo I found an unknown argument: $1
endif
endif
This will be easiest to do with two regex checks, one for validity of all letters and another to look for duplicate letters.
Take a look at this code:
#!/bin/tcsh
echo $1 | grep -q -e "[^csqai]"
if ( $? != 0 ) then
echo "Valid characters"
else
echo "Invalid characters"
endif
echo $1 | grep -q -e "\([csqai]\).*\1"
if ( $? != 0 ) then
echo "No repeated valid characters"
else
echo "Repeated valid characters"
endif
The first regex looks for all characters which are not valid and the second looks for any repeated characters
I don't know how to do these checks in tcsh, so I did them with grep. The -q flag makes grep silent. $? is 0 if no match is found.

How to check whether a string has at least one alphabetic character?

I want to check whether a string has at least one
alphabetic character?
a regex could be like:
"^.*[a-zA-Z].*$"
however, I want to judge whether a string has at least one
alphabetic character?
so I want to use, like
if [ it contains at least one alphabetic character];then
...
else
...
fi
so I'm at a loss on how to use the regex
I tried
if [ "$x"=~[a-zA-Z]+ ];then echo "yes"; else echo "no" ;fi
or
if [ "$x"=~"^.*[a-zA-Z].*$" ];then echo "yes"; else echo "no" ;fi
and test with x="1234", both of the above script output result of "yes", so they are wrong
how to achieve my goal?thanks!
Try this:
#!/bin/bash
x="1234"
y="a1234"
if [[ "$x" =~ [A-Za-z] ]]; then
echo "$x has one alphabet"
fi
if [[ "$y" =~ [A-Za-z] ]]; then
echo "Y is $y and has at least one alphabet"
fi
If you want to be portable, I'd call /usr/bin/grep with [A-Za-z].
Use the [:alpha:] character class that respects your locale, with a regular expression
[[ $str =~ [[:alpha:]] ]] && echo has alphabetic char
or a glob-style pattern
[[ $str == *[[:alpha:]]* ]] && echo has alphabetic char
It's quite common in sh scripts to use grep in an if clause. You can find many such examples in /etc/rc.d/.
if echo $theinputstring | grep -q '[a-zA-Z]' ; then
echo yes
else
echo no
fi

Bash RegEx to check floating point numbers from user input

I'm relatively new to bash programming and i am currently creating a simple calculator.
It needs to use floating point numbers and check they are so.
I have a checkNumbers function:
function checkNumber {
regExp=[0-9]
if [ $testNo =~ $regExp ]
then
echo "That is a number!"
let check=1
else
echo "Damn! Not A Number!"
fi
}
where i get the user to input a number like this:
while [ $check -eq 0]
do
echo "Please enter a number
read testNo
echo ""
checkNumber
done
This does not work, i get this error:
./calculator.sh: line 39: [: =~: binary operator expected
line 39 is:
if [ $testNo =~ $regExp ]
I have tried lots of Regular Expressions like:
^*[0-9]*$
and
^*[0-9]\.[0.9]*$
etc etc.
also, i have tied other ways of performing the check:
case $testNo
in
^*[0-9]*$) echo "That is a number!";;
*) echo "Error! Not a number!";;
esac
and
if [ $testNo = ^*[0-9]*$ ]
then
echo "etc etc"
else
echo "oops"
fi
I also need this to work with floating point numbers.
could someone please let me know how i go about this?
This regex ^[-+]?[0-9]+\.?[0-9]*$ will match only digits with an optional .:
$ echo 30 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo 30.10 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo 30. | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo +30 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo -30 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
I think when you tried ^*[0-9] you wanted ^[0-9]*
Rexeplanation:
^ # Match start of string
[-+]? # Match a leading + or - (optional)
[0-9]+ # Match one or more digit
\.? # Match a literal . (optional, escaped)
[0-9]* # Match zero or more digits
$ # Match the end of the string
Note: this matches numbers followed by a . like 30., not sure if this is acceptable for you.
Edit: Don't quote the regex
testNo=30.00
if [[ $testNo =~ ^[+-]?[0-9]+\.?[0-9]*$ ]]; then
echo Match
fi
>>> Match
To use that type of feature, you need the [[ ... ]] version of the conditional. [ is the "old" test command and doesn't handle regular expressions at all.
#! /bin/bash
function checkNumber {
regExp='^[+-]?([0-9]+\.?|[0-9]*\.[0-9]+)$'
if [[ $testNo =~ $regExp ]]
then
echo "That is a number!"
let check=1
else
echo "Damn! Not A Number!"
fi
}
testNo=1
checkNumber
testNo=-1.2
checkNumber
testNo=+.2
checkNumber
testNo=+0.
checkNumber
testNo=a
checkNumber
testNo=hello2you
checkNumber
$ ./t.sh
That is a number!
That is a number!
That is a number!
That is a number!
Damn! Not A Number!
Damn! Not A Number!
See What is the difference between test, [ and [[ ?.
An explanation on the regex:
^ Anchor at start of string
$ Anchor at end of string
These two make the regex match the whole string passed, partial matches are not allowed.
[+-]
matches either + or -.
[+-]?
makes that part optional, so the above matches exactly +, - or nothing at all.
Then there's an alternation (part1|part2) which will match if part1 or part2 matches.
Part one is:
[0-9]+\.?
which matches one or more (+) digits (but not zero digits/empty set) and an optional .. This handles numbers of the form 123 and 534.. But not just ..
Part two is:
[0-9]*\.[0-9]+
This matches zero or more (*) digits, followed by a ., followed by one or more digits. This matches all other floats like 1.3 or .543 (without exponent notation), but still excludes just ..
#!/bin/bash
#script to validate
while [ true ]; do
clear
echo "Introduce the numeric value: "
read -r var
if [[ $var =~ ^[+-]?([0-9]+)$ ]]
then
echo "You introduced an integer value (without decimals)"
read
else
if [[ $var =~ ^[+-]?([0-9]+\.)$ ]]
then
echo "Incomplete floating value (no values provided at the right of the point)"
read
else
if [[ $var =~ ^[+-]?(\.[0-9]+)$ ]]
then
echo "Incomplete floating value (no values provided at the left of the point)"
read
else
if [[ $var =~ ^[+-]?([0-9]+\.[0-9]+)$ ]]
then
echo "You introduced a correct floating value"
read
else
echo "You introduced something other than a valid numeric value"
read
fi
fi
fi
fi
done
I include an optional dot at the start.
user#debian:~$ testNo=.01
user#debian:~$ if [[ $testNo =~ ^[+-]?[.]?[0-9]+\.?[0-9]*$ ]];
> then
> echo Match;
> fi
Match

shell scripting and regular expression

#!bin/bash
echo enter your password :
read password
passlength=$(echo ${#password})
if [ $passlength -le 8 ];
then
echo you entered correct password
else
echo entered password is incorrect
fi
if [[$password == [a-z]*[0-9][a-z]*]];
then
echo match found
else
echo match not found
fi
I am not getting what's wrong with this code. If I enter any string as a password, let's say hello123, it gives me an error:
hello123 : command not found
What is wrong with my script?
You can do the following to make it work cross-platforms with any the bourne shell (/bin/sh) based shell, no bash specific primitives -
echo "$password" | grep -q "[a-z]*[0-9][a-z]*"
if [ $? -eq 0 ] ;then
echo "match found"
else
echo "match not found"
fi
Also feel free to use quotes around the variable names. It will save you hours and hours worth of useless debugging. :)
Technically it should give you an error like [[hello123 : command not found.
The issue is that [[$password is not expanded how you think it is. Bash will first resolve the $password variable to what you entered (i.e. hello123). This will yield the string [[hello123 which bash will then try to invoke (and fail, as there is nothing with that name).
Simply add a space () after [[ and bash will recognise [[ as the command to run (although it is a builtin).
if [[ "$password" == [a-z]*[0-9][a-z]* ]]
then
...
The corrected script is below. The errors were:
#!/bin/bash, not #!bin/bash
To read password length, just do passlength=${#password}, not
passlength=$(echo ${#password})
Always put a space after [ or [[
#!/bin/bash
echo "enter your password :"
read password
passlength=${#password}
if [[ $passlength -le 8 ]]
then
echo "you entered correct password"
else
echo "entered password is incorrect"
fi
if [[ $password == [a-z]*[0-9][a-z]* ]]
then
echo "match found"
else
echo "match not found"
fi
In the bash [[ construct, the == operator will match glob-style patterns, and =~ will match regular expressions. See the documentation.
#!/bin/bash
read -s -p "Enter Password: " password
password_length=${#password}
if [ $password_length -lt 8 -o $password_length -gt 20 ] ;then
echo -e "Invalid password - should be between 8 and 20 characters in length.";
echo ;
else
# Check for invalid characters
case $password in
*[^a-zA-Z0-9]* )
echo -e "Password contains invalid characters.";
echo ;
;;
* )
echo "Password accepted.";
echo ;
break;
;;
esac
fi
More tuned example..
Try to replace line
if [[$password == [a-z]*[0-9][a-z]*]];
with following
if echo "$password" | grep -qs '[a-z]*[0-9][a-z]*'
HTH