My favorite way to do it is by using /dev/urandom together with tr to delete unwanted characters. For instance, to get only digits and letters:
***
tr -dc A-Za-z0-9
***
Alternatively, to include more characters from the OWASP password special characters list:
***
tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~'
***
If you have some problems with tr complaining about the input, try adding LC_ALL=C like this:
***
LC_ALL=C tr -dc 'A-Za-z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~'
***