【问题标题】:How do I create a temp filename in Erlang?如何在 Erlang 中创建临时文件名?
【发布时间】:2009-08-03 12:30:09
【问题描述】:

我需要将数据放入文件中,因为我的其他函数需要一个文件作为输入。

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

是否存在类似 unix "tempfile" 的东西?

【问题讨论】:

    标签: erlang filenames


    【解决方案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]),但 /tmp 在 Windows 上不可用是重点。

    【讨论】:

    • 函数ensure_dir(Name)filelib 模块中。链接 - erlang.org/doc/man/filelib.html#ensure_dir-1
    • 确实!谢谢你抓住那个。固定的。我可能应该写一篇关于各种文件* 和 io* 模块的博客文章,让人们抢先了解如何在哪里找到什么样的函数。第一次对我来说肯定不是很明显。
    【解决方案2】:

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

    【讨论】:

    • 略丑,但string:strip(os:cmd("mktemp"), right, $\n) 可以避免通常会在最后返回的烦人的隐形换行符。
    • 短版:lib:nonl( os:cmd( "mktemp" ) ).
    • lib:nonl/1OTP 21 中被删除,替换的似乎是string:chomp/1
    【解决方案3】:

    或者你可以这样做

    erlang:phash2(make_ref())

    用于快速简便的唯一标识符。最多可用于 2^82 次调用,这对于您的目的来说应该足够了。我发现这比使用节点名称格式化时间戳更容易。

    【讨论】:

      【解决方案4】:

      您的意思是只生成实际文件名吗?在这种情况下,最安全的方法是混合使用您从 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> 
      

      【讨论】:

      • 因为不是每个人都知道这一点:对 erlang:now() 的调用保证每次都返回唯一的结果。相当有用的属性...
      • 是的,非常有用。这种生成参考的方式还有另一件事非常有用。如果您想首先确保 id 始终是唯一的(您可以将其视为本地引用)并且其次要知道引用的来源,那么您可以在其中获得节点名,这非常有用。在我们的系统中(集群中有 n 个节点和共享数据表等),我们可以仅使用此标识符(节点 + 时间)轻松找到我们需要的有关作业的日志和信息。
      • 如果这样的文件已经存在,它将失败。 erlang:now() 不能保证文件不存在。
      • erlang:now/0 已弃用,请改用erlang:unique_integer/0 (docs)。
      • 这有一个弱点,它不能防止两个并发进程之间的名称冲突。两个进程可以在同一微秒内生成文件名。由于构建工具使用此基于时间戳的 tmpfile 名称,我目前正在处理间歇性构建失败。请改用 mktemp 解决方案。
      【解决方案5】:

      迟到的答案:我刚刚注意到 test_server 模块支持暂存目录,值得一看

      http://www.erlang.org/doc/man/test_server.html#temp_name-1

      【讨论】:

      • 不保证这个文件不会及时存在你会尝试制作这个文件。所以它并不安全。这就是为什么stdlib.h 中有mkstemp。来自 GNU coreutils 的 mktemp 在内部使用 mkstemp 调用。
      【解决方案6】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-19
        • 1970-01-01
        • 2010-11-04
        • 2017-08-11
        • 2012-06-14
        • 2011-03-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多