【发布时间】:2011-02-17 03:20:49
【问题描述】:
我想在 unix shell(比如 tcshell)中生成一个随机文件名。文件名应由随机的 32 个十六进制字母组成,例如:
c7fdfc8f409c548a10a0a89a791417c5
(我将添加任何必要的内容)。关键是能够仅在 shell 中执行此操作,而无需借助程序。
【问题讨论】:
我想在 unix shell(比如 tcshell)中生成一个随机文件名。文件名应由随机的 32 个十六进制字母组成,例如:
c7fdfc8f409c548a10a0a89a791417c5
(我将添加任何必要的内容)。关键是能够仅在 shell 中执行此操作,而无需借助程序。
【问题讨论】:
假设您使用的是 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
【讨论】:
为什么不使用 unix mktemp 命令:
$ TMPFILE=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` && echo $TMPFILE
tmp.MnxEsPDsNUjrzDIiPhnWZKmlAXAO8983
【讨论】:
--dry-run 标志来防止文件被创建。这当然会打开一个可能的竞争条件。
一个命令,没有管道,没有循环:
hexdump -n 16 -v -e '/1 "%02X"' -e '/16 "\n"' /dev/urandom
如果您不需要换行符,例如在变量中使用它时:
hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom
使用“16”生成 32 个十六进制数字。
【讨论】:
hexdump。
正如您可能从每个答案中注意到的那样,您通常必须“求助于程序”。
但是,在 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 的 回答中所述。
【讨论】:
在 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
【讨论】:
16^32。
md5sum 二进制文件的系统。例如,FreeBSD 和 OSX 有一个 /sbin/md5,它的用途相同,但使用不同的选项语法。
uuidgen 会生成这个,除非你必须删除连字符。所以我发现这是实现这一目标的最优雅(至少对我而言)的方式。它应该可以在 linux 和 OS X 上开箱即用。
uuidgen | tr -d '-'
【讨论】:
另一种方式[tm]。
R=$(echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM | md5 | cut -c -8)
FILENAME="abcdef-$R"
【讨论】:
这个答案与 fmarks 非常相似,所以我不能真正相信它,但我发现 cat 和 tr 命令组合很慢,我发现这个版本快了很多。你需要 hexdump。
hexdump -e '/1 "%02x"' -n32 < /dev/urandom
【讨论】:
-n16 来回 32 个字符。
第一个答案很好,但如果不需要,为什么要叉猫。
tr -dc 'a-f0-9' < /dev/urandom | head -c32
【讨论】:
从/dev/random 中获取 16 个字节,将它们转换为十六进制,取第一行,去掉地址,去掉空格。
head /dev/random -c16 | od -tx1 -w16 | head -n1 | cut -d' ' -f2- | tr -d ' '
当然,假设“不借助程序”意味着“只使用现成的程序”。
【讨论】:
od 上有更多的标志,但不能让它做我想做的事。 fmark 的答案虽然它处理更多字节,但实际上可能更快。
-w16 替换为 -N16)。
您可以添加的另一件事是运行 date 命令,如下所示:
date +%S%N
读取非秒时间,结果增加了很多随机性。
【讨论】:
希望为这个话题添加一个(也许)更好的解决方案。
注意:这只适用于bash4 和mktemp 的一些实现(例如,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
【讨论】:
如果您使用的是 Linux,那么 Python 将预先安装。所以你可以选择类似下面的东西:
python -c "import uuid; print str(uuid.uuid1())"
如果您不喜欢破折号,请使用如下所示的替换功能
python -c "import uuid; print str(uuid.uuid1()).replace('-','')"
【讨论】:
如果您的系统中有openssl,您可以使用它来生成具有定义长度的随机十六进制(也可以是-base64)字符串。我发现它非常简单,并且可以在 cron 中用于单行作业。
openssl rand -hex 32
8c5a7515837d7f0b19e7e6fa4c448400e70ffec88ecd811a3dce3272947cb452
【讨论】: