Non dovresti mai usare cat
con /dev/urandom
. Né dovresti usare alcuna utilità progettata per i file di testo.
/dev/urandom
è un flusso continuo di dati casuali. Non produrrà mai una fine del file. Le letture bufferizzate riempiranno il buffer di lettura, quindi anche se stai eseguendo il piping dell'output di cat
in qualche altro programma, la lettura non verrà terminata finché la pipe non sarà chiusa.
Niente di tutto ciò sarebbe altro che inefficiente, tranne che quando leggi /dev/urandom
, stai consumando entropia (casualità), che è una risorsa preziosa. Una volta esaurita l'entropia, /dev/urandom
l'output di sarà meno casuale, il che vanifica lo scopo. (Sarà raccolta più entropia, ma ci vorrà un po' per accumularsi.)
Tutto questo vale il doppio per /dev/random
, perché quando esaurisce l'entropia, di solito si blocca. (Tranne sui sistemi operativi che rendono /dev/random
sinonimo di /dev/urandom
.)
Di conseguenza, dovresti sempre leggere esattamente la quantità di dati casuali di cui hai bisogno e non di più.
A quanto pare, stai mirando a 24 caratteri alfanumerici. Ci sono 62 possibili caratteri alfanumerici; semplificherebbe notevolmente le cose se fossi disposto a consentire ad altri due caratteri di portare il totale a 64. In tal caso, potresti produrre 24 caratteri estraendo 18 byte di casualità e facendoli passare attraverso un codificatore base64. Per estrarre una quantità precisa di dati, usa dd
, progettato per lo scopo:
dd bs=18 count=1 if=/dev/urandom | base64 | tr +/ _.
(Il tr
alla fine traduce i due caratteri non alfanumerici prodotti da base64
in due caratteri diversi che sono più compatibili con i nomi dei file. Solo un suggerimento.)
Se sei determinato a utilizzare esattamente caratteri alfanumerici, potresti utilizzare una strategia di rifiuto simile a quella che stai attualmente utilizzando, ma basata su quanto sopra. Sfortunatamente, non è possibile prevedere esattamente quanto input ti servirà in questo caso, quindi l'approccio più semplice è leggere un po' di più e riprovare nel raro caso in cui non ne hai abbastanza:
# Here we produce 28 characters each time
until s=$(dd bs=21 count=1 if=/dev/urandom |
LC_ALL=C tr -cd A-Za-z0-9)
((${#s} >= 24)); do :; done
# When the loop ends we have at least 24 characters; truncate
s=${s:0:24}
Se non hai bash, puoi sostituire ((${#s} >= 24))
con [ ${#s} -ge 24 ]
e s=${s:0:24}
con s=$(printf %.24s $s)
Ma se stai solo cercando di generare buoni nomi di file casuali, dovresti usare mktemp
, che consente di specificare uno scheletro per i nomi e verifica anche che il nome generato non sia già presente. Vedi man mktemp
.
In realtà cat /dev/urandom
non finisce mai da solo. Ma quando head -1
legge la prima riga, esce, chiudendo così stdin e chiudendo una pipe. OS alza SIGPIPE
a fold
anche esce e così via, quindi cat /dev/urandom
finisce alla fine.
Nel tuo caso, qualcosa che blocca SIGPIPE
, ovvero trap può farlo:
$ trap '' PIPE
$ cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1
7FazO6mnsIow3ylkvEHB55jE
(hungs)
Prova a riattivarlo in subshell:
( trap - PIPE ; cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1 )