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