How do you call an external command (as if I'd typed it at the Unix shell or Windows command prompt) from within Ada program text?
I'd prefer a non-gnat-specific solution, but I'll post what I found on my own as an initial answer anyway.
How do you call an external command (as if I'd typed it at the Unix shell or Windows command prompt) from within Ada program text?
I'd prefer a non-gnat-specific solution, but I'll post what I found on my own as an initial answer anyway.
You could also use system. Supported on e.g.
Note that errors are written to standard error.
main.adb
with Ada.Text_IO;
with Interfaces.C;
procedure Main is
package C renames Interfaces.C;
use type C.int;
function system (command : C.char_array) return C.int
with Import, Convention => C;
command : aliased constant C.char_array :=
C.To_C ("mv README.md README.txt");
result : C.int;
begin
result := system (command);
if result = 0 then
Ada.Text_IO.Put_Line ("OK");
else
Ada.Text_IO.Put_Line ("Failed");
end if;
end Main;
with GNAT.OS_Lib;
procedure main is
Exit_Code : Integer;
begin
Exit_Code := GNAT.OS_Lib.Spawn (Program_Name => "mv", Args => (new String'("README.md"), new String'("README.txt")));
if Exit_Code /= 0 then
raise Program_Error with "Exit code:" & Exit_Code'Image;
end if;
end main;
Change 'mv' to 'move' if you are on windows.
See the GNAT.OS_Lib documentation to extend this for non-blocking and/or capturing the stdout/stderr.
We have a nice Process API with asynchronous notifications. Take a look at an example. It work on Linux, Windows and Mac OS X. There is optional integration with Glib event loop.
Args : Spawn.String_Vectors.UTF_8_String_Vector;
L : aliased Listeners.Listener;
begin
Args.Append ("Hello World");
P.Set_Program ("/bin/echo");
P.Set_Arguments (Args);
P.Set_Working_Directory ("/tmp");
P.Set_Listener (L'Unchecked_Access);
P.Start;
Based on my comments regarding the Florist library (see other answer), I added another example (just for reference and info, not as a serious answer to the original question). Unfortunately, the version of Florist I refer to in the comments does not compile on recent versions of GNAT (e.g. GNAT CE 2020). This version of the library seems to have become somewhat dependent on GNAT's internals which have been modified slightly (so the library is not as compiler independent anymore as originally intended). Nonetheless, for completeness, if you would be able to compile it (using either an older version of GNAT or by repairing the source code), then the example below would spawn a child process without blocking and wait for it to finish.
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with POSIX; use POSIX;
with POSIX.Process_Primitives;
with POSIX.Process_Identification;
procedure Main is
package PP renames POSIX.Process_Primitives;
package PI renames POSIX.Process_Identification;
Filename : constant POSIX_String := "sleep";
Args : POSIX_String_List;
PID : PI.Process_ID;
PT : PP.Process_Template;
PS : PP.Termination_Status;
begin
Append (Args, Filename); -- argv[0]
Append (Args, "5"); -- argv[1]
PP.Open_Template (PT);
PP.Start_Process_Search
(Child => PID,
Filename => Filename,
Template => PT,
Arg_List => Args);
loop
Put_Line ("Waiting...");
PP.Wait_For_Child_Process
(Status => PS,
Child => PID,
Block => False);
exit when PP.Status_Available (PS);
delay 1.0;
end loop;
Put_Line ("The child terminated with status code: "
& PP.Exit_Status_Of (PS)'Image);
PP.Close_Template (PT);
end Main;
output
$./obj/main
Waiting...
Waiting...
Waiting...
Waiting...
Waiting...
Waiting...
The child terminated with status code: 0