简体   繁体   中英

Generate random string where it must have a special character in shell script

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?

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.

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
openssl rand -base64 25

Change 25 to any amount of random characters you want to generate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM