如何在Erlang中创建临时文件名?

9

我需要把数据放进一个文件里,因为我的其他函数需要以文件作为输入。

在Erlang中如何创建唯一的文件名?

类似于Unix的“tempfile”这样的东西是否存在?

6个回答

12
你是说只需要生成实际的文件名吗?在这种情况下,最安全的方法是使用从now()获取的数字和计算机的主机名的混合(如果有多个节点执行相同的操作)。
类似这样:
1> {A,B,C}=now().
{1249,304278,322000}
2> N=node().
nonode@nohost
3> lists:flatten(io_lib:format("~p-~p.~p.~p",[N,A,B,C])).
"nonode@nohost-1249.304278.322000"
4> 

4
并非每个人都知道:调用 erlang:now() 函数保证每次返回唯一结果。这是个相当有用的特性。 - viraptor
非常有用。使用这种生成引用的方式还有一个非常有用的功能,就是可以在其中获取节点名称,如果您想首先确保ID始终唯一(可以将其视为本地引用),并且知道引用来自哪里,则非常有用。在我们的系统中(具有n个群集节点和共享数据表等),我们只需使用此标识符(节点+时间)即可轻松找到作业的日志和所需信息。 - Mazen Harake
如果这样的文件已经存在,它将会失败。erlang:now()无法保证该文件不存在。 - Wacław Borowiec
4
erlang:now/0 已经被弃用,请使用 erlang:unique_integer/0 (文档) 替代。 - clonejo
1
这种方法的弱点在于它不能防止两个并发进程之间的名称冲突。两个进程可能会在同一微秒内生成相同的文件名。我目前正在处理由于构建工具使用基于时间戳的tmpfile名称而导致的间歇性构建失败。请改用mktemp解决方案。 - R Perrin

11

你还可以使用 TMP = lib:nonl(os:cmd("mktemp"))


2
略微丑陋,但是 string:strip(os:cmd("mktemp"), right, $\n) 可以避免通常会在末尾返回的令人讨厌的不可见换行符。 - zxq9
1
简化版:lib:nonl( os:cmd( "mktemp" ) ) - Vojta Rylko
lib:nonl/1OTP 21中已被移除,替代方法似乎是string:chomp/1 - kauron

7

或者你可以使用 erlang:phash2(make_ref()) 来快速、轻松地生成一个唯一标识符。对于你的目的来说,这个标识符在最多 2^82 次调用时是唯一的,应该足够了。我发现这比使用节点名称格式化时间戳更容易。


1
我最近遇到了这个问题——我的用户同时使用Windows和Linux系统,所以旧的经典方法lib:nonl(os:cmd("mktemp"))已经不再适用了。
所以,我采用了以下方法,首先是一个mktemp/1函数,它返回一个可用的文件名;其次是一个mktemp_dir/1函数,它返回一个已创建的目录。
-spec mktemp(Prefix) -> Result
   when Prefix   :: string(),
        Result   :: {ok, TempFile  :: file:filename()}
                  | {error, Reason :: file:posix()}.

mktemp(Prefix) ->
    Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36),
    TempPath = filename:basedir(user_cache, Prefix),
    TempFile = filename:join(TempPath, Rand),
    Result1 = filelib:ensure_dir(TempFile),
    Result2 = file:write_file(TempFile, <<>>),
    case {Result1, Result2} of
         {ok, ok}    -> {ok, TempFile};
         {ok, Error} -> Error;
         {Error, _}  -> Error
    end.

而且目录版本:

-spec mktemp_dir(Prefix) -> Result
   when Prefix  :: string(),
        Result  :: {ok, TempDir   :: file:filename()}
                 | {error, Reason :: file:posix()}.

mktemp_dir(Prefix) ->
    Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36),
    TempPath = filename:basedir(user_cache, Prefix),
    TempDir = filename:join(TempPath, Rand),
    Result1 = filelib:ensure_dir(TempDir),
    Result2 = file:make_dir(TempDir),
    case {Result1, Result2} of
         {ok, ok}    -> {ok, TempDir};
         {ok, Error} -> Error;
         {Error, _}  -> Error
    end.

这两个基本上做的是同样的事情:我们得到一个强随机名称作为二进制,将其转换为base36字符串,并将其附加到操作系统返回给我们作为安全用户本地临时缓存位置的任何内容后面。
在Unix类型系统上,当然我们可以使用filename:join(["/tmp", Prefix, Rand]),但是Windows上/tmp不可用是整个问题的重点。

1
函数 ensure_dir(Name)filelib 模块中。链接 - https://erlang.org/doc/man/filelib.html#ensure_dir-1 - Anatolii Kosorukov
1
的确!感谢你发现了这个问题。已经修复了。我可能应该写一篇博客,介绍各种文件和io模块,让人们有一个头脑风暴,了解如何找到哪种函数在哪里。第一次使用时,这肯定不是很明显。 - zxq9

1

1
它不能保证在尝试创建此文件时该文件将不存在。因此不安全。这就是为什么stdlib.h中有mkstemp的原因。GNU coreutils中的mktemp内部使用mkstemp调用。 - Hynek -Pichi- Vychodil

0
在 OTP 24 中,没有 file:ensure_dir。因此我做了类似的东西:
对于目录:
mktemp_dir(Prefix) ->
    Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36),
    TempDir = filename:basedir(user_cache, Prefix),
    []= os:cmd("mkdir " ++ "\"" ++ TempDir ++ "\""),
    {ok, _} = file:list_dir(TempDir),
    TempDir.

文件名称:

mktemp(Prefix) ->
    Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36),
    TempDir = filename:basedir(user_cache, Prefix),
    TempFile = filename:join(TempDir, Rand),
    []= os:cmd("mkdir " ++ "\"" ++ TempDir ++ "\""),
    {ok, _} = file:list_dir(TempDir),
    Result = file:write_file(TempFile, <<>>),
    case {Result} of
         {ok}    -> {ok, TempFile};
         {Error}  -> Error
    end.

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