Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a temp filename in Erlang?

I need to put data in a file since my other function takes a file as input.

How do I create a unique filename in Erlang?

Does something like unix "tempfile" exist?

like image 353
Fu. Avatar asked Aug 03 '09 12:08

Fu.


4 Answers

I've finally had this problem -- and my user is using a mix of Windows and Linux systems, so the old tried-and-true lib:nonl(os:cmd("mktemp")) method is just not going to cut it anymore.

So here is how I've approached it, both with a mktemp/1 function that returns a filename that can be used and also a mktemp_dir/1 function that returns a directory (after having created it).

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

And the directory version:

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

Both of these do basically the same thing: we get a strongly random name as a binary, convert that to a base36 string, and append it to whatever the OS returns to us as a safe user-local temporary cache location.

On a unix type system, of course, we could just use filename:join(["/tmp", Prefix, Rand]) but the unavailability of /tmp on Windows is sort of the whole point here.

like image 135
zxq9 Avatar answered Oct 15 '22 06:10

zxq9


You can also use TMP = lib:nonl(os:cmd("mktemp")).

like image 34
Hynek -Pichi- Vychodil Avatar answered Nov 14 '22 23:11

Hynek -Pichi- Vychodil


Do you mean just generate the acutal filename? In that case the safest way would be to use a mix of the numbers you get from now() and the hostname of your computer (if you have several nodes doing the same thing).

Something like:

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])).
"[email protected]"
4> 
like image 12
Mazen Harake Avatar answered Nov 15 '22 00:11

Mazen Harake


Or you could do

erlang:phash2(make_ref())

for a quick and easy unique indentifier. Unique for up to 2^82 calls which should be enough.for your purposes. I find this easier than formatting a timestamp with node name for use.

like image 7
Jeremy Wall Avatar answered Nov 15 '22 00:11

Jeremy Wall