Reputation: 501
I'm using /dev/urandom to generate the special string but have seen that sometimes special character is not included in the generated string :
cat /dev/urandom | tr -dc 'a-zA-Z0-9$%&%' | fold -w 12 | head -n 1
Output: a8y34XFXC5ar
My requirement is that the generated string output (by using /dev/urandom) must contain one upper case, one lower case, one special character and one numeric character.
Please suggest how to achieve this requirement where every generated string must meet the mentioned requirement ?
Upvotes: 0
Views: 3502
Reputation: 83
openssl rand -base64 25
Change 25
to any amount of random characters you want to generate.
Upvotes: 0
Reputation: 20002
When you must have 4 different types of characters in your output, make 4 strings or arrays (for each type 1), use random to select one character out of each string/array and append it like you already did to length 12.
And shuf
the 12 characters when you don't want to have the limit the possible strings to them having the 4 different types as the first 4 characters.
Something like
printf -v small "%s" {a..z}
printf -v large "%s" {A..Z}
printf -v digit "%s" {0..9}
special='@#$%^&*+=<>?' # Edit: Single quotes, not double quotes
# Debug loop
for s in small large digit special; do
echo "str=${!s}"
done
get4() {
for s in small large digit special; do
echo "${!s}" | sed 's/./&\n/g' | grep . | shuf | head -1
done| tr -d '\n'
}
passw=$(echo "$(get4)$(cat /dev/urandom | tr -dc 'a-zA-Z0-9$%&%' | fold -w 8 | head -n 1)" |
sed 's/./&\n/g' | grep . | shuf | tr -d '\n')
echo "${passw}"
After the sed
the stream will end with 2 newlines, we only want one. So grep .
Upvotes: 2
Reputation: 37404
One in awk:
awk '{
for(i=1;i<=length();i++) { # read a line, iterate chars
chr=substr($0,i,1) # get a char
if(chr~/[a-z]/&&(a<=2||((b>=1)&&(c>=1)&&(d>=1)))) { # if in char class a-z
str=str chr # append
a++ # keep count to get
} # ... from each class
if(chr~/[A-Z]/&&(b<=2||((a>=1)&&(c>=1)&&(d>=1)))) {
str=str chr
b++
}
if(chr~/[0-9]/&&(c<=2||((a>=1)&&(b>=1)&&(d>=1)))) {
str=str chr
c++
}
if(chr~/[$%&]/&&(d<=2||((a>=1)&&(b>=1)&&(c>=1)))) {
str=str chr
d++
}
if(length(str)==12) {
print str
exit
}
}
}' /dev/urandom
As the character classes are not the same size and only 3 is allowed from each class before all classes are represented, the random is probably biased.
This one gets accepted characters from /dev/urandom
(well, it's readind from a file so where ever you point it to), and 'print`s all matches until you ctrl-c it:
awk '{
for(i=1;i<=length();i++) {
chr=substr($0,i,1)
if(chr~/[a-zA-Z0-9$%&]/)
str=str chr
}
if(length(str)<12)
next
else
for(i=1;i<=length(str)-11;i++) {
sstr=substr(str,i,12)
if(sstr~/[a-z]/ && sstr~/[A-Z]/ && sstr~/[0-9]/ && sstr~/[$%&]/){
print substr(str,i,12)
i+=11
}
}
str=""
}' /dev/urandom
Upvotes: 1