我使用以下行生成随机脚本:
export MY_VAR="nxf-$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1)"
这很好用,但是当我将它包含在BASH脚本中并执行它时,脚本执行挂起。
流程树显示以下流程:
4045 ? S 0:00 bash .command.run
4046 ? R 22:38 \_ cat /dev/urandom
4047 ? S 1:03 \_ tr -dc a-zA-Z0-9
4048 ? S 0:34 \_ fold -w 24
似乎urandom
永远不会退出。为什么会这样?
答案 0 :(得分:7)
永远不要将cat
与/dev/urandom
一起使用。您也不应该使用任何专为文本文件设计的实用程序。
/dev/urandom
是连续的随机数据流。它永远不会产生文件结束。缓冲读取将填充读缓冲区,因此即使您将cat
的输出汇总到其他程序中,读取也不会被终止,直到管道关闭。
除了当你阅读/dev/urandom
时,你正在使用熵(随机性),这是一种宝贵的资源。一旦熵被用完,/dev/urandom
的输出将不那么随机,这就失去了目的。 (将收集更多的熵,但建立起来需要一段时间。)
/dev/random
所有这一切都变了两倍,因为当它耗尽熵时,它通常会阻塞。 (使/dev/random
成为/dev/urandom
的同义词的操作系统除外。)
因此,您应该始终准确读取所需的随机数据量,而不是更多。
显然,你的目标是24个字母数字字符。有62个可能的字母数字字符;如果你愿意允许其他两个字符使总数达到64,那么它会大大简化。在这种情况下,你可以通过提取18个字节的随机性并将其传递给base64编码器来产生24个字符。要提取精确数量的数据,请使用专为此目的设计的dd
:
dd bs=18 count=1 if=/dev/urandom | base64 | tr +/ _.
(最后tr
将base64
生成的两个非字母数字字符转换为两个不同的字符串,这些字符对文件名更友好。只是一个建议。)
如果您决定使用精确的字母数字字符,则可以使用类似于您当前使用的拒绝策略,但基于上述情况。遗憾的是,在这种情况下,您无法准确预测您需要多少输入,因此最简单的方法是多读一点,并在极少数情况下重试您不要得到足够的:
# 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}
如果您没有bash,可以将((${#s} >= 24))
替换为[ ${#s} -ge 24 ]
,将s=${s:0:24}
替换为s=$(printf %.24s $s)
但是,如果您只是尝试生成好的随机文件名,则应使用mktemp
,它允许您为名称指定骨架,并验证生成的名称是否已存在。请参阅man mktemp
。
答案 1 :(得分:5)
实际上cat /dev/urandom
永远不会自行结束。但是当head -1
读取第一行时,它会退出,从而关闭stdin并关闭管道。操作系统提升SIGPIPE
到fold
也会退出,依此类推,因此cat /dev/urandom
最终会结束。
在你的情况下,阻止SIGPIPE
的东西,即陷阱可以做到这一点:
$ trap '' PIPE
$ cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1
7FazO6mnsIow3ylkvEHB55jE
(hungs)
尝试在子shell中重新启用它:
( trap - PIPE ; cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1 )