count how many times characters from a range occur in a string

215 Views Asked by At

So I'm kinda new to bash and scripting in general. I'm a student and we've been given an assignment to create a password checker that checks whether or not the arguments at $1 of the command meet the password criteria. If $1 is blank, the script should generate an acceptable password (that part is done thanks very much to another thread on here).

Where I am having a problem, is with the checking the conditions of $1 because of one specific criteria for the password. The password criteria is as follows:

  • At least one uppercase letter
  • At least one lowercase letter
  • At least one digit
  • $1 size must be at least 8 characters, and less than 16
  • One AND ONLY one symbol in the range of "@#$%&*-=+"

I have the script set up as a series of nested of if statements that works its way down eliminating possibilities as they come up -- i.e. if the size is good but something else is wrong proceed to the next 'level'; if the size is wrong but everything else is good, echo the size is wrong, etc.

My script correctly identifies Uppercase, lowercase, digit, size, and that there is at least one symbol, but I cannot figure out how to check for one and only one special character.

My current line of thinking is to run $1 into a grep for the range of characters, and then if the count is greater than, or less than 1, give an error. The closest I've come is this:

echo $1 | grep -o "[@#$%&*-=+]" | wc -l

But that outputs 9 -- which I assume is the size of the range of characters.

Any suggestions?

3

There are 3 best solutions below

1
On BEST ANSWER

- has a special meaning in a character class, it denotes a range. Move it to the beginning or end of the class:

$ echo :/, | grep -o "[@#$%&*-=+]" | wc -l
3
$ echo :/, | grep -o "[@#$%&*=+-]" | wc -l
0
$ echo :/, | grep -o "[-@#$%&*=+]" | wc -l
0
1
On

For what it's still worth, here's a solution with all checks done running a single process.

For Linux:

expr \( \
        \( + "$1" : '.*[A-Z]' \) \& \
        \( + "$1" : '.*[a-z]' \) \& \
        \( + "$1" : '.*[0-9]' \) \& \
        \( + "$1" : '.*[-@#$%&*=+]' \) \& \
        \( ${#1} \>= 8 \) \& \
        \( ${#1} \<= 16 \) \
    \) != 0 \& \
    \( + "$1" : '.*[-@#$%&*=+].*[-@#$%&*=+]' \) = 0 >/dev/null && \
echo ok

For *BSD:

expr \( \
        \( "$1" : '.*[A-Z]' \) \& \
        \( "$1" : '.*[a-z]' \) \& \
        \( "$1" : '.*[0-9]' \) \& \
        \( "$1" : '.*[-@#$%&*=+]' \) \& \
        \( ${#1} \>= 8 \) \& \
        \( ${#1} \<= 16 \) \
    \) != 0 \& \
    \( "$1" : '.*[-@#$%&*=+].*[-@#$%&*=+]' \) = 0 >/dev/null && \
echo ok
0
On
$ myword="a!uuu??7889%"
$ res="${myword//[^,!#%&*?@^|+-]}"
$ echo "The original word : $myword"
$ echo "Special characters : $res"
$ echo "number of special characters : ${#res}"

The result is this 

The original word : a!uuu??7889%
Special characters : !??%
number of special characters : 4