为什么 tr 无法在 OSX 上读取 /dev/urandom ?

为什么 tr 无法在 OSX 上读取 /dev/urandom ?

一位同事建议通过以下命令创建随机密钥:

tr -dc A-Za-z0-9_\!\@\#\$\%\^\&\*\(\)-+= < /dev/urandom | head -c 32 | xargs

它给了我错误:

tr:非法字节序列

我担心我的系统上没有/dev/urandom。我尝试用谷歌搜索来找出如何安装这个文件,但结果却是空的。我尝试了一下locate urandom,也一无所获。 (实际上,它找到了手册页,但这没有帮助)

如何urandom在我的 Mac OSX 系统上使用? (狮子)

答案1

根据您收到的错误消息,我认为/dev/urandom这不是问题。如果是的话,我预计会出现类似的错误no such file or directory

我搜索了您收到的错误消息并发现了此消息,这似乎可能与您的问题相关:nerdbynature.de 2010-04-11 tr-Illegal-byte-sequence(Web Archive 的 2019-09 快照)

tr基本上,通过在命令前面添加LC_CTYPE=C(或LC_ALL=C,请参阅注释)来指定区域设置:

LC_CTYPE=C tr -dc A-Za-z0-9_\!\@\#\$\%\^\&\*\(\)-+= < /dev/urandom | head -c 32 | xargs

答案2

tr尝试将其输入解释为 UTF-8 编码的文本。因此,它会抱怨并中止第一个字节序列不是有效的 UTF-8。前缀或会tr将该变量导出到 的环境中,从而将其本地字符集的想法更改为 C 标准,即一切都只是不透明字节的序列。LC_ALL=CLC_CTYPE=Ctr

顺便问一下,\)-+你的命令中的序列是故意的吗?这*也包括你已经包括的内容,但没有-像你希望的那样包括它自己。最好写一个这样的:

LC_ALL=C tr -dc 'A-Za-z0-9_!@#$%^&*()\-+=' < /dev/urandom
LC_CTYPE=C tr -dc A-Za-z0-9_\!\@\#\$\%\^\&\*\(\)\\-+= < /dev/urandom

答案3

正如其他人所指出的,您的问题不是/dev/urandom缺少,而是如何tr在 OS X 上工作。不要乱搞环境变量,而是使用perl以下内容tr

perl -pe 'binmode(STDIN, ":bytes"); tr/A-Za-z0-9_\!\@\#\$\%\^\&\*\(\)-+=//dc;' < /dev/urandom | head -c 32; echo

其优点是可以跨 OS X、Redhat 和 Ubuntu 移植。

(我还删除了管道xargs,替换 女巫echo,以在输出末尾获得换行符。)

答案4

您的语言环境的字符编码(您可以使用 来判断locale charmap)是每个字符一个多字节。

如今最常见的是 UTF-8,其中字符可以编码为 1 到 4 个字节。并非所有字节序列都会形成 UTF-8 中的有效字符。 UTF-8 中的每个非 ASCII 字符都以设置了两个最高位的一个字节开始,并说明后面有多少个设置了最高位(但不是第二高位)的字节。

/dev/urandom包含随机字节流。tr音译字符,因此需要将这些字节解码为字符。您范围内的那些 ASCII 字符均以 UTF-8 编码在一个字符上,但tr仍需要对所有字符进行解码。例如,还有其他多字节编码,其中某些字符不A包含 0x41 字节( 的代码A)。

因为该随机字节流必然包含无效序列(例如,0x80 字节本身在 UTF-8 中是无效的,因为非 ASCII 字符必须以大于 0xc1 的字节开头(0xc0 和 0xc1 在非 UTF-8 中不存在) 8 个字符)),因此tr当发生这种情况时会返回错误。

这里您想要的是将字节流视为每个字符一个字节的编码中的字符。无论您选择哪个并不重要,因为您范围内的所有这些字符(假设 AZ,您的意思是 ABCDEFGHIJKLMNOPQRSTUVWXYZ 而不是诸如Ý,之类的Ê字符)都是可移植字符集的一部分,因此在系统支持的所有字符集中进行相同的编码。

为此,您需要设置LC_CTYPE本地化变量,该变量决定使用哪种字符集以及字符类包含哪些blank内容alpha。但对于 AZ 范围的定义,您还需要设置变量LC_COLLATE(决定字符串排序的变量)。

C又名区域设置POSIX是保证字符为单字节且 AZ 为 ABCDEFGHIJKLMNOPQRSTUVWXYZ 的区域设置。你可以这样做:

 LC_CTYPE=C LC_COLLATE=C tr -dc 'A-Za-z0-9_!@#$%^&*()+=-'

(这里将 移动-到末尾,否则)-+将被视为一个范围,如A-Z

但请注意,LC_ALL变量会覆盖所有其他LC_*LANG变量。因此,如果LC_ALL已经定义,则上述操作将不起作用。因此,您可以简单地执行以下操作:

 LC_ALL=C tr -dc 'A-Za-z0-9_!@#$%^&*()+=-'

这会影响其他事情,例如错误消息的语言,但无论如何,更改 LC_CTYPE 可能已经成为错误消息的问题(例如,无法在 C 语言环境的字符集中表达俄语或日语错误消息)。

相关内容