Bash en * nix (109)
while ! grep -Pq [A-Z].*[a-z].*[0-9].*[\\W_]<<<$a$a$a$a
do a=`tr -dc !-~</dev/urandom|head -c15`
done
echo $a
Para que funcione correctamente, $ano se debe establecer una contraseña válida pero no aleatoria por adelantado. Si desea incluir a=una línea en el frente, son tres caracteres más, pero le permite ejecutar la cosa repetidamente. Obviamente, también puede reemplazar todas las líneas nuevas por ;lo que tiene una línea que puede ejecutar con la frecuencia que desee.
Además, debería haber establecido LC_ALL=Co no establecer variables de entorno específicas de la localidad ( LANGy LC_CTYPEen particular), ya que los rangos de caracteres dependen de que el orden de clasificación sea igual al orden ASCII.
/dev/urandomes la fuente de bytes aleatorios. !-~es el rango de todos los caracteres permitidos, como se especifica en la pregunta. tr -dcelimina todos los caracteres que no figuran en su siguiente argumento. headtoma 15 de los personajes restantes. grepcomprueba si cada uno de los tipos requeridos ocurre al menos una vez. Su entrada consiste en cuatro copias del candidato, por lo que el orden de los símbolos no importa, por lo tanto, todas las contraseñas posibles tienen la posibilidad de ser seleccionadas. El -qto grep suprime la salida.
Por razones desconocidas, en /dev/randomlugar de /dev/urandomlleva años. Parece que la entropía se agotó bastante rápido. Si cden /dev, puede evitar algunos más bytes, pero que se siente un poco como hacer trampa.
Pitón 2 (138)
import re,random
a=''
while not re.search('[A-Z].*[a-z].*[0-9].*[\W_]',a*4):
a=''.join(random.sample(map(chr,range(33,127))*15,15))
print a
Para que el código sea legible, agregué una nueva línea y una sangría después del ciclo que no es necesario y que no conté.
Esta es esencialmente la misma idea que en la versión bash. La fuente aleatoria aquí es random.sample, que no repetirá elementos. Para contrarrestar este hecho, utilizamos 15 copias de la lista de letras permitidas. De esa manera, toda combinación puede ocurrir, aunque las que tienen letras repetidas ocurrirán con menos frecuencia. Pero decido considerar esto como una característica, no como un error, ya que la pregunta no requería la misma probabilidad para todas las permutaciones, solo la posibilidad.
Pitón 3 (145)
import re,random
a=''
while not re.search('[A-Z].*[a-z].*[0-9].*[\W_]',a*4):
a=''.join(random.sample(list(map(chr,range(33,127)))*15,15))
print(a)
Una nueva línea y una sangría nuevamente no cuentan. Además de algunos gastos generales de sintaxis específicos de Python-3, esta es la misma solución que para Python 2.
JavaScript (161)
a=[];for(i=33;i<127;)a.push(s=String.fromCharCode(i++));
while(!/[A-Z].*[a-z].*[0-9].*[\W_]/.test(s+s+s+s))
for(i=0,s="";i<15;++i)s+=a[Math.random()*94|0];alert(s)
Agregué las nuevas líneas para facilitar la lectura, pero no las conté.
R (114)
s<-""
while(!grepl("[A-Z].*[a-z].*[0-9].*(\\W|_)",paste(rep(s,4),collapse="")))
s<-intToUtf8(sample(33:126,15,T))
s
Salto de línea y sangría dentro del bucle agregado pero no contado. Si lo desea, puede moverlo nuevamente a una sola ;línea separada.