在Unix shell中生成一个随机的文件名

96

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

c7fdfc8f409c548a10a0a89a791417c5

(我会添加必要的内容)。重点是只能使用shell而不需要使用程序来完成。


请参考_如何在bash中创建UUID?_。 - Dennis Williamson
14个回答

140

假设您使用的是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

1
这个解决方案对我来说真的很奇怪,因为它在实际的随机哈希后面添加了一个白色背景的“%”符号,但由于我的shell在某些情况下通常表现奇怪,我不想在被接受之前就让它看起来很糟糕 :) - LukeN
1
我在Mac上尝试了这个,它有一个/dev/urandom。在bash shell中执行该命令会导致错误 -“tr:非法字节序列”。 - Gareth Stockwell
5
我认为这里的问题在于BSD和Mac将该字符串解释为多字节而不是单字节。我没有机器来尝试这个命令,所以如果这个命令有效,请在此处回报:cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c 32 - fmark
3
看起来又是另一个毫无意义的猫的使用例子 :-) - Jens
1
@LukeN 你可能已经在5年后弄清楚了,但是'%'只是表示该行没有换行符而已。http://unix.stackexchange.com/a/167600/105858 - John P
显示剩余3条评论

51

为什么不使用Unix的mktemp命令:

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

6
在这种情况下,您不仅会生成字符串,还会创建一个文件。 - Igor Chubin
5
有些实现会有一个--dry-run标志来防止文件被创建。这当然会引起可能的竞态条件。 - glenn jackman
虽然这不是什么大问题,但即使指定了--try-tun参数,此实用程序仍会进行额外的IO检查以测试磁盘上是否存在XXXX文件(只是一个stat)。这只是一个小考虑因素,也许对于极大的便利来说,这是微不足道的权衡。 - oᴉɹǝɥɔ

24

一个命令,不需要管道和循环:

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

如果您不需要换行符,例如在将其用作变量时:

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

使用 "16" 会生成 32 个十六进制数字。


这太棒了。我从未想过可以使用 hexdump 来做到这一点。 - avetisk

12

uuidgen生成的结果与此完全相同,只是需要删除连字符。 因此,我发现这是实现这一目标最优雅(至少对我来说)的方法。它应该可以在Linux和OS X上直接使用。

uuidgen | tr -d '-'

8

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

然而,在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 +)中无法工作,因为在这些shell中,RANDOM仅在大括号扩展中求值一次。
请注意,由于对值(范围从0到32767)使用模运算符,因此使用这些代码片段产生的数字分布可能会有所偏差(更不用说这些数字本身就是伪随机的了)。但是,由于我们使用模16和32768可以被16整除,因此这里不会有问题。
无论如何,正确的方法是像Oleg Razgulyaev的答案中那样使用mktemp

1
谢谢Dennis,这对我很有帮助。我只是将字符串设置为空,这样每次重新运行相同的脚本时,字符串的长度始终符合预期.. randomId=$(string="";for i in {0..5}; do string+=$(printf "%x" $(($RANDOM%16)) ); done; echo $string); echo "随机ID值为${randomId}" - Santosh Kumar Arjunan
@SantoshKumarA:感谢您指出变量需要初始化。我已将其添加到我的答案中。我还添加了另一种有趣的技术和解释。 - Dennis Williamson
不使用任何 bash 进程外的调用,这对我来说是最好的答案。比接受的答案更优先。 - Blindleistung

6
在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

1
这将只生成32768个唯一的文件名,而不是16^32 - Dennis Williamson
这也仅适用于包含md5sum二进制文件的系统。例如,FreeBSD和OSX有一个/sbin/md5,它具有相同的目的但使用不同的选项语法。 - ghoti

4

另一种方式[tm]。

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

大多数系统上可用的程序是md5sum。我喜欢这个解决方案,因为如果你不需要高随机性,只需使用$RANDOM一次,这将成为最简洁、最易读的解决方案。 - user1115652
a) 如果您有md5sum,可以将md5替换为md5sum并相应更新剪切。 b) 仅使用一个$RANDOM是危险的,它只产生0到32767之间的值,这对于应用程序来说太短了,具体取决于应用场景。 - reto

4

这个答案与fmarks非常相似,所以我无法真正地为它负责,但我发现猫和tr命令组合相当慢,而我发现这个版本要快得多。您需要使用hexdump。

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

Ace。使用-n16来表示32个字符。 - Walf

3

您可以添加的另一件事是运行以下日期命令:

date +%S%N

读取纳秒时间并将结果添加到大量随机数中。


3
第一个答案很好,但如果不需要,为什么要分叉cat。
tr -dc 'a-f0-9' < /dev/urandom | head -c32

这个问题虽然举了tcsh的例子,但是它是在一个通用UNIX shell的背景下。你的版本则是针对shell扩展的特定版本。 - oᴉɹǝɥɔ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接