Home > front end >  How to generate a random string without repeated characters?
How to generate a random string without repeated characters?

Time:11-30

Im trying to generate a password in Bash that matches MacOS password requirements and one of them is that it can't have repeated characters (aa, bb, 44, 00, etc).

I know i can use openssl rand -base64 or /dev/urandom and use tr -d to manipulate the output string. I use grep -E '(.)\1{1,}' to search for repeated characters but if i use this regex to delete (tr -d (.)\1{1,}'), it deletes the entire string. I even tried tr -s '(.)\1{1,}' to squeeze the characters to just one occurrence but it keep generating repeated characters in some attempts. Is it possible to achieve what i'm trying to?

P.S.: that's a situation where i cant download any "password generator tool" like pwgen and more. It must be "native"

CodePudding user response:

Sorry I have no bash at hands to, but trying to help you.

What about iteratively grabbing the unique chars, eg. by

chars=$(openssl rand -base64)
pwd=

for (( i=0; i<${#chars}; i   )); do
  if [[ "$(echo $pwd | grep "${chars:$i:1}")" == "" ]]; then
    pwd=$pwd${chars:$i:1}
  fi
done

CodePudding user response:

The issue might be that you have non-printable characters, so it's not actually repeated. If you first get the first e.g. 30 characters, then delete any non-alphanumeric, non punctuation characters, then squeeze any of those characters, then from whatever is left get the first 20 characters, it seems to work:

cat /dev/urandom | tr -dc '[:alnum:][:punct:]' | fold -w ${1:-30} | head -n 1 | tr -s '[:alnum:][:punct:]' | cut -c-20

Output e.g.:

]'Zc,fs6m;wUo%wLIG%K
2O3Ff4dzi30~L.RH8jR0
sU?,WkK]&I;z'|eTSLjY
5gK]\H51i@Rtux.{bdC=
:g"\?5JsjBd1r])2^WR 
;{cR:jY\rIc&Q(2yo:|-
fFykmxvZ|ATX_l6L(8h:
^Sd*,V%9}bWnTYNv"w?'
6foMgbU6:n<*cWj2W=3&
*v39FWmB@LwE5O`a3C36

CodePudding user response:

Is there a specific size requirement? Other required characters?

How about -

openssl rand -base64 20 | sed -E 's/(.)\1 /\1/g'

CodePudding user response:

You're getting close. tr doesn't use regex (but does use POSIX character classes).

Either of these will squeeze repeats:

tr -cs '\0'
tr -s '[:graph:][:space:]'

They differ only in how we refer to "all characters". First is "complement of null" second is all printable and all white space characters. There may be a neater way to specify "all characters".

Or using sed:

sed -E 's/(.)\1 /\1/g'

This both squeezes printable characters, and removes white space:

tr -ds '[:space:]' '[:graph:]'

Example for 32 non whitespace characters, with no repeats:

tr -ds '[:space:]' '[:graph:]' < /dev/urandom |
dd bs=32 count=1

Also, this example specifies a list of allowed characters (letters, digits, and _.), then squeezes any repeats:

tr -dc '[:alnum:]_.' < /dev/urandom |
tr -sc '\0' |
dd bs=32 count=1

Example output:

9mCEqrhHPwmq7.1qEky6qn4jqzDpRK7b

Putting dd at the end means we get 32 characters after removing repeats. You may also want to add status=none to hide dd logging on stderr.

  • Related