【问题标题】:A random string generator in a bash script isn't respecting the number of given charactersbash 脚本中的随机字符串生成器不考虑给定字符的数量
【发布时间】:2014-05-23 19:26:27
【问题描述】:

我正在尝试在 osx 10.8.5 上的 bash 脚本中构建一个随机字符生成器。目标是为脚本生成随机字符串,为 wordpress wp-config.php 文件生成盐。 sn-p 如下所示:

#!/bin/bash -e
read -p "Number of digits: " digits

function rand_char {
    take=$(($RANDOM % 88)); i=0; echo {a..z} {A..Z} {0..9} \, \; \. \: \- \_ \# \* \+ \~ \! \§ \$ \% \& \( \) \= \? \{ \[ \] \} \| \> \< | while read -d\  char;
    do
        [ "$i" = "$take" ] && echo "$char\c";
        ((i++));
    done
}

function rand_string {
    c=$1;
    while [ $c -gt 0 ];
        do char="${char}"$(rand_char);
        let c=$c-1;
    done
    echo $char
}

outputsalt=`rand_string $digits`

echo $outputsalt

如果我输入 64 作为位数,则每次尝试生成的字符数都不同:

HeF6D>z}x[v=s(qRoPmNkLiIfG7E5C3A1yZwWtU§S~Q*O_M:J,b86|4]2{0)X&p    (63 chars)
WtUrSpQnOkLiJ,H8F6D4B1yZ)X&V$T!R+P_M:e;c9a7>5}2{u=s(q%o§m~j#hIfG   (64 chars)
_g:d,b86|4]w{u=r&p$n!lMjKhIeFcDaB>z0xYt)r&p§m~kLiJgHeFcDA1yZwX     (62 chars)
}w{u=s(q%oPmNkKhIfGdEbC3A1xYvWtUrS~Q*O_L.J,H8F6|4]2?Z)X&V$n!l+j_   (64 chars)
l+j_g:e;cDaB>z}x{u=sTqRoPmNkKhIfG7E5C3A1xYvW%U§S~Q*O_L.J,b86|4]    (63 chars)

有没有办法让字符数保持在给定的数字上?最好的问候拉尔夫

【问题讨论】:

  • 这不是问题的原因,但您应该将$(rand_char) 放在引号内,以防止通配符扩展。
  • 也许是个愚蠢的问题......但是你到底为什么不使用数组???

标签: bash random


【解决方案1】:

尝试一个更简单的案例:

function rand_char {
  take=$(($RANDOM % 2)); i=0; echo a b | while read -d\  char;
  do
    [ "$i" = "$take" ] && echo "$char\c";
    ((i++));
  done
}

它将产生a 和空白,但不会产生b

我们可以将问题进一步简化为:

echo a b | while read -d\  char; do echo "$char"; done

它只写a 而不是b。这是因为您指示read 最多读取一个空格,而b 之后没有空格,所以它失败了。这意味着每 88 个字符中的一个字符将被丢弃,从而导致您的行略短。

最简单的解决方法是在末尾添加一个虚拟参数以强制使用空格:

echo {a..z} {A..Z} {0..9} (etc etc) \} \| \> \< '' | while read ...
#                                       Here ---^

请注意,您的方法只是将 15 位熵添加到盐中,而绝对最小值应为 64。这样做更容易和更安全的方法是:

LC_CTYPE=C tr -cd 'a-zA-Z0-9,;.:_#*+~!@$%&()=?{[]}|><-' < /dev/urandom | head -c 64

(注意:这会将您的 unicode 段落符号替换为 ascii @)

【讨论】:

  • +1 我注意到take=87时添加字符失败,但我无法像你一样弄清楚原因。
  • 基于tr 的命令是一个很好的选择,但是(至少在OSX 上)它会以illegal byte sequence 失败,除非你在它前面加上LC_CTYPE=C(或者,更可靠的是@987654335 @):LC_ALL=C tr -cd 'a-zA-Z0-9,;.:_#*+~!@$%&amp;()=?{[]}|&gt;&lt;-' &lt; /dev/urandom | head -c 64
  • 非常感谢您的解释、澄清和解决方案!
  • 还要感谢 @mklement0 对 OSX 解决方法的评论。正好碰到那个;)
【解决方案2】:

无意冒犯,你的编码风格是,呃……,不是我见过的最好的:)

#!/bin/bash -e

read -p "Number of digits: " digits
# TODO: test that digits is really a number

chars=( {a..z} {A..Z} {0..9} \, \; \. \: \- \_ \# \* \+ \~ \! \§ \$ \% \& \( \) \= \? \{ \[ \] \} \| \> \< )

function rand_string {
    local c=$1 ret=
    while((c--)); do
        ret+=${chars[$((RANDOM%${#chars[@]}))]}
    done
    printf '%s\n' "$ret"
}

outputsalt=$(rand_string $digits)

echo "$outputsalt"

【讨论】:

  • 我承认“不是最好的”还是太委婉了。这或多或少是我第一次接触 bash 脚本。在我的第一次迭代中,我尝试让事情基本上正常工作。对我来说还有改进的空间;)感谢您更优雅地解决这个问题:)
【解决方案3】:

我发现您的脚本中有两个问题。

我很确定

take=$(($RANDOM % 88));

应该是

take=$(($RANDOM % 87));

否则,您似乎将超出输入流的末尾。

另一个问题是这个字符:

Bash 将其视为两个字符(宽字符?)。我会从你的可能性中删除它。

当然,这意味着上面的行将是:

take=$(($RANDOM % 86));

实际上,做这两件事对我有用。

已编辑:

@那个人有更好的答案。添加空格而不是减少模数将确保您获得每个字符

【讨论】:

  • 至于§(段号):你说得对;具体来说:它的 Unicode 代码点是 0xa7,在 7 位 ASCII 范围之外;在 UTF8 编码(OSX 上的默认编码)中,您会得到 2 个字节,即0xc20xa7
【解决方案4】:

如果您喜欢单行,另一种方法:

perl -le 'print map { ("a".."z","A".."Z",0..9,",",";",".",":","-","_","#","*","+","~","!","§","\$","%","&","(",")","=","?","{","}","[","]","|","<",">") [rand 87] } 1..63'

或者,因为您可能不希望在末尾换行:

perl -e 'print map { ("a".."z","A".."Z",0..9,",",";",".",":","-","_","#","*","+","~","!","§","\$","%","&","(",")","=","?","{","}","[","]","|","<",">") [rand 87] } 1..63'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-19
    • 2011-12-29
    • 2010-09-26
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多