为什么/ cat/urandom挂了我的bash脚本?

pdi*_*aso 7 linux bash

我正在使用以下行生成随机脚本:

export MY_VAR="nxf-$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1)"
Run Code Online (Sandbox Code Playgroud)

这工作正常,但当我将它包含在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
Run Code Online (Sandbox Code Playgroud)

似乎urandom永远不会退出.为什么会这样?

ric*_*ici 9

你永远不应该使用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 +/ _.
Run Code Online (Sandbox Code Playgroud)

(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} 
Run Code Online (Sandbox Code Playgroud)

如果你没有bash中,你可以替换((${#s} >= 24))使用[ ${#s} -ge 24 ],并s=${s:0:24}s=$(printf %.24s $s)

但是,如果您只是尝试生成好的随机文件名,则应该使用mktemp,这允许您为名称指定骨架,并验证生成的名称是否已存在.见man mktemp.


mya*_*aut 5

实际上cat /dev/urandom永远不会自己结束.但是当head -1读取第一行时,它会退出,从而关闭stdin并关闭管道.OS提高SIGPIPEfold也退出,等等,所以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)
Run Code Online (Sandbox Code Playgroud)

尝试在子shell中重新启用它:

( trap - PIPE ; cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 24 | head -n 1 )
Run Code Online (Sandbox Code Playgroud)