我以为这将是一个愚蠢的简单问题,但事实证明有点复杂。
从 awk 的inside,我想按需快速生成一串 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
是真的很慢——除了少量的字符串之外无法使用。
知道如何修改其中一个 awks 以便
顺便说一句:这个问题已经被问过几次了:
使用 awk 1 并执行以下操作:
time awk -v r=$RANDOM '
function rand_string(i) {
s=""
min=48
max=123
#srand(res) Duh!! WRONG! Only use srand once or it resets to the same sequence
while (length(s)<i) {
c=sprintf("%c", int(min+rand()*(max-min+1)))
if (c~/[[:alnum:]]/) s=s c
}
return s
}
BEGIN{
srand(r) # Use srand ONCE only
for (i=1; i<=1000000; i++) {print rand_string(20)}
}' | uniq -c | awk '$1>1'
# No output so no duplicates
real 0m9.813s
user 0m10.413s
sys 0m0.074s
VS 红宝石:
time 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..1_000_000).each{|_| puts rand_string(20)}' | uniq -c | awk '$1>1'
# no output so no duplicates
real 0m12.954s
user 0m13.441s
sys 0m0.217s
Ruby 的时间增加可能是管道的剩余部分,这与 awk 相同。所以 awk 更快一点...