83

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

c7fdfc8f409c548a10a0a89a791417c5

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

4

14 回答 14

132

假设您在 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
于 2010-05-08T11:22:39.237 回答
45

为什么不使用 unix mktemp 命令:

$ TMPFILE=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` &&  echo $TMPFILE
tmp.MnxEsPDsNUjrzDIiPhnWZKmlAXAO8983
于 2010-05-08T11:28:20.970 回答
21

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

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

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

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

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

于 2012-05-30T20:25:16.247 回答
7

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

但是,不使用任何外部可执行文件,在 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 整除,所以这不是问题。

无论如何,正确的方法是使用mktempOleg Razgulyaev 的答案。

于 2010-05-08T12:49:55.427 回答
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
于 2010-05-08T11:15:07.407 回答
6

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

uuidgen | tr -d '-'
于 2019-08-16T14:35:59.587 回答
4

还有另一种方式[tm]。

R=$(echo $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM | md5 | cut -c -8)
FILENAME="abcdef-$R"
于 2013-05-31T15:48:52.710 回答
3

这个答案与 fmarks 非常相似,所以我不能真正相信它,但我发现 cat 和 tr 命令组合很慢,而且我发现这个版本要快一些。你需要十六进制转储。

hexdump -e '/1 "%02x"' -n32 < /dev/urandom
于 2014-07-21T19:49:42.233 回答
3

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

tr -dc 'a-f0-9' < /dev/urandom | head -c32
于 2018-04-13T23:27:54.383 回答
2

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

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

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

于 2010-05-08T11:28:55.853 回答
2

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

date +%S%N

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

于 2016-07-11T22:52:00.760 回答
1

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

注意:这仅适用于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
于 2015-06-09T15:43:51.140 回答
1

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

 openssl rand -hex 32
 8c5a7515837d7f0b19e7e6fa4c448400e70ffec88ecd811a3dce3272947cb452
于 2019-01-02T15:15:20.390 回答
1

如果你在 Linux 上,那么 Python 将预先安装。所以你可以去类似下面的东西:

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

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

python -c "import uuid; print str(uuid.uuid1()).replace('-','')"
于 2016-08-11T22:13:20.220 回答