Is it possible to execute a command before creating the file in which I want to redirect its output

I want to execute the following command:

dune exec -- ocaml-print-intf file.ml

And redirect its output to file.mli

The issue is that I can not write

dune exec -- ocaml-print-intf file.ml > file.mli

Because file.mli is created then dune exec -- ocaml-print-intf file.ml is executed and its output is redirected to file.mli. Why is that an issue? Because it’s supposed to generate the signature of file.ml but the first thing it checks is if there is already a signature file (file.mli in our case) and if there is it outputs it.

Example:

❯ dune exec -- ocaml-print-intf src/file.ml
val a : int
val b : string
❯ dune exec -- ocaml-print-intf src/file.ml > src/file.mli
❯ cat src/file.mli

❯ dune exec -- ocaml-print-intf src/file.ml

I found a solution with sponge

❯ dune exec -- ocaml-print-intf src/file.ml | sponge src/file.mli
❯ cat src/file.mli
val a : int
val b : string

But I was wondering if there was another solution not necessitating to install an external software.

Asked By: Lhooq

||

You noticed that it works with sponge:

dune exec -- ocaml-print-intf src/file.ml | sponge src/file.mli

This is due to sponge not creating the src/file.mli name until all the data has been collected.

You can do it without sponge by simply redirecting to a differently named file and then moving this to the correct name when the data has been collected:

dune exec -- ocaml-print-intf src/file.ml >tmpfile &&
mv tmpfile src/file.mli

This assumes that tmpfile can be used as an intermediate file. If dune fails, then the intermediate file has to be cleaned up. The following takes this into consideration:

if dune exec -- ocaml-print-intf src/file.ml >tmpfile
then
    mv tmpfile src/file.mli
else
    rm tmpfile
fi

Using mktemp to create the temporary file:

tmpfile=$(mktemp)

if dune exec -- ocaml-print-intf src/file.ml >"$tmpfile"
then
    mv "$tmpfile" src/file.mli
else
    rm "$tmpfile"
fi
Answered By: Kusalananda

With ksh93, you can do:

dune exec -- ocaml-print-intf src/file.ml >; src/file.mli

>;word
Write output to a temporary file. If the command completes successfully, rename it to word, otherwise, delete the temporary file. >;word cannot be used with the exec built-in.

You can also emulate sponge in one line of perl code:

dune exec -- ocaml-print-intf src/file.ml |
  perl -0777 -spe 'open STDOUT, ">", $out or die "$out: $!n"' -- -out=src/file.mli

Which like sponge stores the whole input in memory before dumping it into the output file once eof is found on input.

In zsh, you could do:

mv =(dune exec -- ocaml-print-intf src/file.ml) src/file.mli

Where =(cmd) expands to the path of a temp file that contains the output of cmd. Beware permissions on the file will be restricted.

Answered By: Stéphane Chazelas
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.