You could add the special characters with another shuf
, just can't use brace expansion and ranges like that.
But you could explicitly list them, one by one, quoted to protect them from the shell:
shuf -r -n2 -e '!' '"' '#' '$' % '&' '(' ')' '*' + , - . / : ';' '<' = '>' '?' @ '[' '\' ']' '^' _ '{' '|' '}'
Or shove them in a string and use word-splitting to get them into multiple arguments. Need to disable globbing with set -f
, though, otherwise the asterisk will cause issues:
set -f
shuf -r -n2 -e $(sed -e 's/./& /g' <<< '!"#$%&()*+,-./:;<=>?@[\]^_{|}' )
(or just do what everyone does, and add two fixed special characters at the end of an otherwise sensible password that's long enough even without them.)
To make sure you have a letter as the first character, it's easiest to just add one separately, and shuffle the characters of only the rest of the password. E.g. this would build the password in two pieces, the first being a single letter (upper or lower case), the second is what you had in the comments:
#!/bin/bash
set -f
pw=$(shuf -r -n1 -e {A..Z} {a..z})
pw="$pw$( { shuf -r -n4 -e {A..Z}; shuf -r -n4 -e {a..z}; shuf -r -n4 -e {0..9}; shuf -r -n2 -e $(sed -e 's/./& /g' <<< '@%+/!#$^?:.(){}[]-_.'); } | shuf | tr -d '\n' )"
echo "$pw"
The output would be something like this:
$ bash pw.sh
WRgpJ7pP%Tj60.1
$ bash pw.sh
oV6N#7s4Po3Bt)r
now the issue that I have is that numbers or special characters can not be the first character in the password, I can remove last shuf but this will have first all upper case, then all lowercase, then numbers and last the special characters, I need something like put at least 1 letter and then doesn't matter the order
– Chris Sandoval Hidalgo Oct 27 '21 at 14:27