你如何在 Ada 中调用外部程序?

How do you call an external program in Ada?

如何从 Ada 程序文本中调用外部命令(就像我在 Unix shell 或 Windows 命令提示符下输入的一样)?

我更喜欢非特定于 gnat 的解决方案,但无论如何我都会 post 我自己找到的作为初始答案。

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;

如果您在 windows,请将 'mv' 更改为 'move'。

请参阅 the GNAT.OS_Lib documentation 以将其扩展为非阻塞 and/or 捕获 stdout/stderr。

您也可以使用 system。支持

请注意,错误会写入标准错误。

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;

根据我对 Florist 库的评论(参见 ),我添加了另一个示例(仅供参考和信息,不是对原始问题的认真回答)。不幸的是,我在评论中提到的 Florist 版本无法在最新版本的 GNAT(例如 GNAT CE 2020)上编译。这个版本的库似乎在某种程度上依赖于 GNAT 的内部结构,这些内部结构经过了轻微的修改(因此该库不再像最初预期的那样独立于编译器)。尽管如此,为了完整起见,如果您能够编译它(使用旧版本的 GNAT 或通过修复源代码),那么下面的示例将在不阻塞的情况下生成一个子进程并等待它完成。

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;

输出

$./obj/main
Waiting...
Waiting...
Waiting...
Waiting...
Waiting...
Waiting...
The child terminated with status code:  0

我们有一个很好的进程 API,带有异步通知。看看an example。它适用于 Linux、Windows 和 Mac OS X。可以选择与 Glib 事件循环集成。

   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;