从awk内部,我想根据需要快速生成一个相当随机(即随机但不加密)的 X 个字母数字字符串。
在 Ruby 中,我可以这样做:
ruby -e '
def rand_string(len, min=48, max=123, pattern=/[[:alnum:]]/)
rtr=""
while rtr.length<len do
rtr+=(0..len).map { (min + rand(max-min)).chr }.
select{|e| e[pattern] }.join
end # falls out when min length achieved
rtr[0..len]
end
(0..5).each{|_| puts rand_string(20)}'
印刷:
7Ntz5NF5juUL7tGmYQhsc
kaOzO1aIxkW5rmJ9CaKtD
49SpdFTibXR1WPWV7li6c
PT862YZQd0dOIaFOIY0d1
vYktRXkdsj38iH3s2WKI
3nQZ7cCVEXvoaOZvm6mTR
为了进行时间比较,Ruby 可以在大约 9 秒内生成 1,000,000 个唯一字符串(无重复)。
考虑到这一点,我在 awk 中尝试了:
awk -v r=$RANDOM '
# the r value will only be a new seed each invocation -- not each f call
function rand_string(i) {
s=""
min=48
max=123
srand(r)
while (length(s)<i) {
c=sprintf("%c", int(min+rand()*(max-min+1)))
if (c~/[[:alnum:]]/) s=s c
}
return s
}
BEGIN{ for (i=1; i<=5; i++) {print rand_string(20)}}'
这不起作用——相同的种子,相同的字符串结果。打印:
D65CsI55zTsk5otzSoJI
D65CsI55zTsk5otzSoJI
D65CsI55zTsk5otzSoJI
D65CsI55zTsk5otzSoJI
D65CsI55zTsk5otzSoJI
现在尝试/dev/urandom
阅读od
:
awk '
function rand_string(i) {
arg=i*4
cmd="od -A n -t u1 -N " arg " /dev/urandom" # this is POSIX
# ^ ^ unsigned character
# ^ ^ count of i*4 bytes
s=""
min=48
max=123
while (length(s)<i) {
while((cmd | getline line)>0) {
split(line, la)
for (e in la) {
if (la[e]<min || la[e]>max) continue
c=sprintf("%c", la[e])
if (c~/[[:alnum:]]/) s=s c
}
}
close(cmd)
}
return substr(s,1,i)
}
BEGIN {for(i=1;i<=5;i++) print rand_string(20) }'
一切按预期进行。打印:
sYY195x6fFQdYMrOn1OS
9mv7KwtgdUu2DgslQByo
LyVvVauEBZU2Ad6kVY9q
WFsJXvw8YWYmySIP87Nz
AMcZY2hKNzBhN1ByX7LW
但是现在的问题是管道od -A n -t u1 -N " arg " /dev/urandom
真的很慢——除了少量的字符串之外无法使用。
知道如何修改其中一个 awk 以便:
- 在大多数平台上运行(即默认 POSIX 套件);
- 可以快速生成X长度的合理随机字符串。
这个问题已被问过几次:
- 如何使用 awk 将一个字符串替换为一个长度为 48 个字符的随机字母数字字符串,答案是使用外部工具——太慢了;
- 用 awk 用随机模式替换给定的模式,但这是一个随机整数并且不使用
srand
; - 在 awk 内部执行命令(生成随机字符串)但再次使用 shell 管道(太慢)和仅限 Linux。