【问题标题】:Generate a random filename in unix shell在 unix shell 中生成一个随机文件名
【发布时间】:2011-02-17 03:20:49
【问题描述】:

我想在 unix shell(比如 tcshell)中生成一个随机文件名。文件名应由随机的 32 个十六进制字母组成,例如:

c7fdfc8f409c548a10a0a89a791417c5

(我将添加任何必要的内容)。关键是能够仅在 shell 中执行此操作,而无需借助程序。

【问题讨论】:

标签: unix shell random tcsh


【解决方案1】:

假设您使用的是 linux,以下应该可以工作:

cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32

如果您的系统熵低,这只是伪随机的,但(在 linux 上)保证会终止。如果您需要真正的随机数据,请使用 /dev/random 而不是 /dev/urandom。此更改将使您的代码阻塞,直到有足够的熵可用以产生真正的随机输出,因此它可能会减慢您的代码。对于大多数用途,/dev/urandom 的输出是足够随机的。

如果您使用的是 OS X 或其他 BSD,则需要将其修改为以下内容:

cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c 32

【讨论】:

  • @LukeN:你的很好。从技术上讲,这个解决方案不能保证终止:)
  • 这个解决方案实际上对我做了一些奇怪的事情,因为它在实际随机哈希之后附加了一个白色背景的“%”符号,但是因为我的 shell 在某些情况下通常表现得很奇怪,所以我没有在被接受之前不想让它看起来很糟糕:)
  • 我在 Mac 上试过这个,它有一个 /dev/urandom。在 bash shell 中执行命令会导致错误 - 'tr: Illegal byte sequence'
  • 我认为这里的问题是 BSD 和 Mac 将字符串解释为多字节而不是单字节。我没有机器可以尝试这个,所以如果可行,请在此处报告:cat /dev/urandom |环境 LC_CTYPE=C tr -cd 'a-f0-9' |头 -c 32
  • 看起来像另一个无用的 cat :-)
【解决方案2】:

为什么不使用 unix mktemp 命令:

$ TMPFILE=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` &&  echo $TMPFILE
tmp.MnxEsPDsNUjrzDIiPhnWZKmlAXAO8983

【讨论】:

  • 在这种情况下,您不仅会生成字符串,还会创建文件。
  • 一些实现有一个--dry-run 标志来防止文件被创建。这当然会打开一个可能的竞争条件。
【解决方案3】:

一个命令,没有管道,没有循环:

hexdump -n 16 -v -e '/1 "%02X"' -e '/16 "\n"' /dev/urandom

如果您不需要换行符,例如在变量中使用它时:

hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom

使用“16”生成 32 个十六进制数字。

【讨论】:

  • 这太棒了。我从没想过为此使用hexdump
【解决方案4】:

正如您可能从每个答案中注意到的那样,您通常必须“求助于程序”。

但是,在 Bash 和 ksh 中,不使用任何外部可执行文件

string=''; for i in {0..31}; do string+=$(printf "%x" $(($RANDOM%16)) ); done; echo $string

在 zsh 中:

string=''; for i in {0..31}; do string+=$(printf "%x" $(($RANDOM%16)) ); dummy=$RANDOM; done; echo $string

将格式字符串中的小写x更改为大写X,使字母十六进制字符变为大写。

这是在 Bash 中执行此操作但没有显式循环的另一种方法:

printf -v string '%X' $(printf '%.2s ' $((RANDOM%16))' '{00..31})

在下文中,“第一”和“第二”printf 指的是它们的执行顺序,而不是它们在行中出现的顺序。

此技术使用大括号扩展来生成一个包含 32 个随机数的列表,每个数为 16,每个数字后跟一个空格,大括号中的一个数字后跟另一个空格(例如 11 00)。对于该列表的每个元素,第一个 printf 使用其格式字符串 (%.2) 去除除前两个字符之外的所有字符,留下单个数字后跟一个空格或两个数字。格式字符串中的空格确保每个输出数字之间至少有一个空格。

不引用包含第一个printf 的命令替换,以便执行分词并且每个数字作为单独的参数转到第二个printf。在那里,数字通过%X 格式字符串转换为十六进制,并且它们彼此附加没有空格(因为格式字符串中没有空格),结果存储在名为string的变量中。

printf 接收的参数多于其格式字符串所占的参数时,格式将依次应用于每个参数,直到它们全部被消耗。如果参数较少,则忽略不匹配的格式字符串(部分),但这不适用于这种情况。

我在 Bash 3.2、4.4 和 5.0-alpha 中对其进行了测试。但它在 zsh (5.2) 或 ksh (93u+) 中不起作用,因为 RANDOM 在这些 shell 的大括号扩展中只被评估一次。

请注意,由于在 0 到 32767 范围内的值上使用 mod 运算符,使用 sn-ps 的数字分布可能会出现偏差(更不用说数字是 首先是随机的)。但是,由于我们使用的是 mod 16 并且 32768 可以被 16 整除,所以这不是问题。

无论如何,正确的做法是使用mktemp,如Oleg Razgulyaev 的 回答中所述。

【讨论】:

  • 谢谢丹尼斯,它帮助了我.. 我只是将字符串设置为空,这样字符串的长度总是如预期的那样,每次重新运行相同的脚本时都会追加.. randomId=$ (string="";for i in {0..5}; do string+=$(printf "%x" $(($RANDOM%16)) ); done; echo $string); echo "随机 ID 值为 ${randomId}"
  • @SantoshKumarA:感谢您指出需要初始化变量。我将其添加到我的答案中。我还添加了另一种有趣的技术并对其进行了解释。
【解决方案5】:

在 zsh 中测试,应该可以与任何 BASH 兼容的 shell 一起使用!

#!/bin/zsh

SUM=`md5sum <<EOF
$RANDOM
EOF`

FN=`echo $SUM | awk '// { print $1 }'`

echo "Your new filename: $FN"

例子:

$ zsh ranhash.sh
Your new filename: 2485938240bf200c26bb356bbbb0fa32
$ zsh ranhash.sh
Your new filename: ad25cb21bea35eba879bf3fc12581cc9

【讨论】:

  • 这只会生成 32768 个唯一的文件名,而不是 16^32
  • 这也仅适用于包含md5sum 二进制文件的系统。例如,FreeBSD 和 OSX 有一个 /sbin/md5,它的用途相同,但使用不同的选项语法。
【解决方案6】:

uuidgen 会生成这个,除非你必须删除连字符。所以我发现这是实现这一目标的最优雅(至少对我而言)的方式。它应该可以在 linux 和 OS X 上开箱即用。

uuidgen | tr -d '-'

【讨论】:

    【解决方案7】:

    另一种方式[tm]。

    R=$(echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM | md5 | cut -c -8)
    FILENAME="abcdef-$R"
    

    【讨论】:

    • 大多数系统上可用的程序是 md5sum。我喜欢这个解决方案,因为如果你不需要高随机性,你可以只使用一次 $RANDOM,这是最简洁最易读的解决方案。
    • a) 如果你有 md5sum,你可以用 md5sum 替换 md5 并相应地更新剪切。 b) 只使用一个 $RANDOM 是危险的,它只产生 0 到 32767 之间的值,这取决于应用程序。
    【解决方案8】:

    这个答案与 fmarks 非常相似,所以我不能真正相信它,但我发现 cat 和 tr 命令组合很慢,我发现这个版本快了很多。你需要 hexdump。

    hexdump -e '/1 "%02x"' -n32 < /dev/urandom
    

    【讨论】:

    • 王牌。使用 -n16 来回 32 个字符。
    【解决方案9】:

    第一个答案很好,但如果不需要,为什么要叉猫。

    tr -dc 'a-f0-9' < /dev/urandom | head -c32
    

    【讨论】:

      【解决方案10】:

      /dev/random 中获取 16 个字节,将它们转换为十六进制,取第一行,去掉地址,去掉空格。

      head /dev/random -c16 | od -tx1 -w16 | head -n1 | cut -d' ' -f2- | tr -d ' '
      

      当然,假设“不借助程序”意味着“只使用现成的程序”。

      【讨论】:

      • 我对此进行了测试。它比其他解决方案慢很多,但确实有效。
      • 是的,对于这样简单的事情来说,这是一个相当长的管道。我希望在od 上有更多的标志,但不能让它做我想做的事。 fmark 的答案虽然它处理更多字节,但实际上可能更快。
      • 如果使用 /dev/urandom 而不是 /dev/random 会更快
      • fmark 是对的,它可能是这里的 /dev/random 部分,因为 /dev/random 是一个真正的随机数生成器,当没有更多熵时会阻塞:)
      • 这个方案绝对是最便携的方案!它甚至可以在最新的 Android 上运行(除非您应该将 -w16 替换为 -N16)。
      【解决方案11】:

      您可以添加的另一件事是运行 date 命令,如下所示:

      date +%S%N
      

      读取非秒时间,结果增加了很多随机性。

      【讨论】:

        【解决方案12】:

        希望为这个话题添加一个(也许)更好的解决方案。

        注意:这只适用于bash4mktemp 的一些实现(例如,GNU 那个)

        试试这个

        fn=$(mktemp -u -t 'XXXXXX')
        echo ${fn/\/tmp\//}
        

        这个速度是head /dev/urandom | tr -cd 'a-f0-9' | head -c 32 的两倍,是cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32 的八倍。

        基准测试:

        使用 mktemp:

        #!/bin/bash
        # a.sh
        for (( i = 0; i < 1000; i++ ))
        do
            fn=$(mktemp -u -t 'XXXXXX')
            echo ${fn/\/tmp\//} > /dev/null
        done
        
        time ./a.sh 
        ./a.sh  0.36s user 1.97s system 99% cpu 2.333 total
        

        还有一个:

        #!/bin/bash
        # b.sh
        for (( i = 0; i < 1000; i++ ))
        do
            cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 32 > /dev/null
        done
        
        time ./b.sh 
        ./b.sh  0.52s user 20.61s system 113% cpu 18.653 total
        

        【讨论】:

          【解决方案13】:

          如果您使用的是 Linux,那么 Python 将预先安装。所以你可以选择类似下面的东西:

          python -c "import uuid; print str(uuid.uuid1())"
          

          如果您不喜欢破折号,请使用如下所示的替换功能

          python -c "import uuid; print str(uuid.uuid1()).replace('-','')"
          

          【讨论】:

            【解决方案14】:

            如果您的系统中有openssl,您可以使用它来生成具有定义长度的随机十六进制(也可以是-base64)字符串。我发现它非常简单,并且可以在 cron 中用于单行作业。

             openssl rand -hex 32
             8c5a7515837d7f0b19e7e6fa4c448400e70ffec88ecd811a3dce3272947cb452
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2022-01-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-01-03
              • 2015-01-22
              相关资源
              最近更新 更多