我想在 unix shell(比如 tcshell)中生成一个随机文件名。文件名应由随机的 32 个十六进制字母组成,例如:
c7fdfc8f409c548a10a0a89a791417c5
(我将添加任何必要的内容)。关键是能够仅在 shell 中执行此操作,而无需借助程序。
假设您在 linux 上,以下应该可以工作:
cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32
如果您的系统熵低,这只是伪随机的,但(在 linux 上)保证会终止。如果您需要真正的随机数据,请使用 cat/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
一个命令,没有管道,没有循环:
hexdump -n 16 -v -e '/1 "%02X"' -e '/16 "\n"' /dev/urandom
如果您不需要换行符,例如在变量中使用它时:
hexdump -n 16 -v -e '/1 "%02X"' /dev/urandom
使用“16”生成 32 个十六进制数字。
正如您可能从每个答案中注意到的那样,您通常必须“求助于程序”。
但是,不使用任何外部可执行文件,在 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 个随机数 mod 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 运算符,使用片段的数字分布可能会出现偏差(更不用说数字首先是伪随机的事实)。但是,由于我们使用的是 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
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 -e '/1 "%02x"' -n32 < /dev/urandom
第一个答案很好,但如果不需要,为什么要叉猫。
tr -dc 'a-f0-9' < /dev/urandom | head -c32
从 中获取 16 个字节/dev/random
,将它们转换为十六进制,取第一行,删除地址,删除空格。
head /dev/random -c16 | od -tx1 -w16 | head -n1 | cut -d' ' -f2- | tr -d ' '
当然,假设“不求助于程序”意味着“仅使用现成的程序”。
您可以添加的另一件事是运行 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
如果您openssl
的系统中有您可以使用它来生成具有定义长度的随机十六进制(也可以是-base64
)字符串。我发现它非常简单,并且可以在 cron 中用于单行作业。
openssl rand -hex 32
8c5a7515837d7f0b19e7e6fa4c448400e70ffec88ecd811a3dce3272947cb452
如果你在 Linux 上,那么 Python 将预先安装。所以你可以去类似下面的东西:
python -c "import uuid; print str(uuid.uuid1())"
如果您不喜欢破折号,请使用如下所示的替换功能
python -c "import uuid; print str(uuid.uuid1()).replace('-','')"