首页 > 解决方案 > 如何仅使用符合 POSIX 的 shell 工具生成字母数字字符串?

问题描述

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

有很多指南展示了如何做到这一点,但我发现没有一个指南接近正确。特别是,我一直看到 head -c 的使用(POSIX 中未定义 -c 参数),如下所示:

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

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

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

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

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

标签: stringshellposix

解决方案


只需使用awkPOSIX 授权提供的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,请运行 asawk -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

推荐阅读