I was trying to create a temporary directory in OCaml. I cannot find a function similar to Filename.temp_file, and also I try to avoid calling platform-dependent system commands, so here is a naive version I write:
let mk_temp_dir () =
let rand_num = Random.int 1000000 |> string_of_int in
let tmp_dir = Filename.get_temp_dir_name () ^ "/" ^ rand_num in
try
Unix.mkdir tmp_dir 0o600;
tmp_dir
with _ -> raise (Sys_error "Cannot create temp dir")
It works OK, but I suspect if it is as robust as Filename.temp_file, or if there is already any existing common practice to create temporary directories. Any help will be much appreciated. Thanks!
Your approach is in essence what bos does, with some caveats.
Note that catching all exceptions is not good practice (though it’s very easy to forget) - you may catch Sys.break which you definitely don’t mean to in a library function like this.
Unsurprisingly, the bos code is a textbook demonstration of how exceptions around a Unix syscall should be dealt with: see src/bos_os_dir.ml#L153-155
Here is a revised version according to the implementation of bos:
let rand_digits () =
let rand = Random.State.(bits (make_self_init ()) land 0xFFFFFF) in
Printf.sprintf "%06x" rand
let mk_temp_dir ?(mode=0o700) ?dir pat =
let dir = match dir with
| Some d -> d
| None -> Filename.get_temp_dir_name ()
in
let raise_err msg = raise (Sys_error msg) in
let rec loop count =
if count < 0 then raise_err "mk_temp_dir: too many failing attemps" else
let dir = Printf.sprintf "%s/%s%s" dir pat (rand_digits ()) in
try (Unix.mkdir dir mode; dir) with
| Unix.Unix_error (Unix.EEXIST, _, _) -> loop (count - 1)
| Unix.Unix_error (Unix.EINTR, _, _) -> loop count
| Unix.Unix_error (e, _, _) ->
raise_err ("mk_temp_dir: " ^ (Unix.error_message e))
in
loop 1000
It returns the name of a fresh temporary directory: “/tmp/patXXXXXX”, with permissions 0o700. The temporary directory is guaranteed to be different from any other directory that exists when mk_temp_dir was called; raise Sys_error if the directory could not be created.