如何仅使用 POSIX 兼容的 shell 工具生成字母数字字符串?

How can I generate an alphanumeric string using only POSIX-compliant shell tools?

我正在尝试使用 POSIX 兼容的 shell 工具生成八个字符的字母数字字符串。我正在尝试使用 /dev/urandom 来生成此字符串(顺便说一下,我不知道 /dev/random 和 /dev/urandom 未由 POSIX 指定,但我要去因为它们存在于 Linux、FreeBSD、Mac OS、AIX 等)

有大量指南说明了如何做到这一点,但我发现 none 几乎是正确的。特别是我一直看到 head -c 的使用(-c 参数未在 POSIX 中定义),如下所示:

head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo ''

我担心的另一个问题是,几乎 none 我见过的解决方案都尊重字节流和字符流之间的区别,这让我担心我生成的字符串不安全。

这是我能想到的最好的,但我并不完全理解它:

strings -n 1 < /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1

它符合 POSIX 标准(减去 /dev/urandom)但我是否正确地达到了目标?如果是这样,是否有更好的方法来实现这一目标?如果有一种方法可以在没有 /dev/urandom 的情况下生成随机字符串,那也会很酷,但我想我在做梦。

试试这个:

head -n 1 /dev/urandom | hexdump | head -n 1 | sed ‘s/0+| //g’ | sed ‘s/\(......\).*//g’

只需使用 awk,POSIX 要求提供 rand 功能。

$ cat password.awk
BEGIN {
    srand();
    chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    s = "";
    for(i=0;i<8;i++) {
        s = s "" substr(chars, int(rand()*62), 1);
    }
    print s
}
$ awk -f password.awk
Cl7A4KVx

(这几乎可以肯定地被代码打成更短的东西;我的 awk 技能有些有限。)

如果您需要多个密码,请将所需的数字作为参数传递,这样您就不需要每秒 运行 awk 多次:

BEGIN {
  srand();
  chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  for(i=0;i<n;i++) {
    s = "";
    for(j=0;j<8;j++) {
      s = s "" substr(chars, int(rand()*62), 1);
    }
    print s
  }
}

传递值n,运行作为awk -v n=5 -f password.awk生成5个密码。

或者,您可以将不同的种子直接传递给 awk:

BEGIN {
  srand(seed);
  chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  s = "";
  for(i=0;i<8;i++) {
      s = s "" substr(chars, int(rand()*62), 1);
  }
  print s
}

awk -v seed=$newseed -f password.akw。请注意,newseed 本身每次都需要有不同的值,但使用简单的连续种子序列就足够了。

seed=$(date +%s)
awk -v seed=$seed -f password.awk; seed=$((seed + 1))
awk -v seed=$seed -f password.awk; seed=$((seed + 1))
awk -v seed=$seed -f password.awk; seed=$((seed + 1))
# etc

八个 "random" 字母数字字符,只有 POSIX 工具箱?很好,一个挑战!

让我们从观察 Base-64 encoding uses almost only alphanumerics (and + and /) and the wonderful POSIX people have added Base-64 support with -m to the uuencode 效用开始。

接下来,我们从哪里得到一些(pseudo?几乎?)随机性?再次观察当前进程列表是相当随意的,特别是如果它包含长格式的所有进程,CPU 时间等等。

我们使用 POSIXly cksum 实用程序进一步压缩 ps 熵,因为 ps 输出的第一行非常恒定,我们必须寻找uuencoded 输出中的合适行(杂乱无章)。

到目前为止我们有

$ ps|cksum|uuencode -m -
begin-base64 644 -
MjUwMDcyMjY3NSAxOTExCg==
====

运行 这几次我们注意到(或知道)第二行中的前两个字符不是很随机,因为它们包含该行的长度。所以我们用

从第2行的位置3提取8个字符
$ ps|cksum|uuencode -m -|sed -n 's,[+/],0,g; 2s/^..\(........\).*//p'
kzNDkzMj
$ ps|cksum|uuencode -m -|sed -n 's,[+/],0,g; 2s/^..\(........\).*//p'
I0NTcxND

并将不需要的 +/ 转置为 0。几乎可以保证每次调用都会得到不同的结果,因为三重管道中命令的进程 ID 与每个 运行 不同。

瞧!不需要 /dev/urandom :-)