How to run external command from OCaml script

I found this topic when encountering a similar problem, and experienced several challenges I did not expect to face. Still, I found the discussion extremely helpful!

I’ve spent several hours trying to figure out things that were not documented and I haven’t found anywhere else, so I would like to share my findings to save someone else some time (especially beginners).

  1. Close channels only after reading them.
    This is mentioned in the middle of the discussion but I wanted to highlight this separately.
  2. Unix.open_process_full doesn’t pass environment variables to the running process.
    So if you expect the running command to have access to env variables like $HOME, you need to set them explicitly.
  3. Use Unix.system to run commands with all env variables passed and original output preserved.
    If you don’t care about reading the output of the process, the Unix.system function is the easiest way to run external processes from an OCaml program.
  4. Base doesn’t have the command function in the Sys module.
    Two modules in base and OCaml stdlib are different but still look the same. I’ve spent an unreasonable amount of time trying to figure out why I can use Sys.command when in fact I had open Base. Some StackOverflow answers mention this function for running external processes. Use Unix.system instead.
  5. You can’t preserve the original interleaved output of stdout and stderr when reading the process output.
    Or at least, I haven’t found the way. But usually the process output text to both stdout and stderr in arbitrary order, and you can’t preserve this order if you reading from the corresponding handles independently.

Based on the above, I’ve implemented a small interface for running external processes and reading their output in my recent tool:

3 Likes