【问题标题】:GNAT.Command_Line.Getopt - how to enforce all command line options were valid?GNAT.Command_Line.Getopt - 如何强制所有命令行选项都有效?
【发布时间】:2020-11-06 10:36:52
【问题描述】:

按照AdaCore Gem #138 : Master the Commandline,我正在尝试创建一个只接受两个选项的命令行程序。

我将一个字符串传递给 getopt,它说明哪些选项是有效的:

Getopt ("-project= -help")

这意味着可以给出“--project foo”,也可以不带参数给出“--help”。我想非常严格 - 项目需要一个参数,而帮助不应该带一个参数。但是,上面似乎很乐意接受(并忽略)命令行上给出的其他参数,只要它们不以减号开头:

$ ./getopt_invalid_option.exe --project foo these options are all invalid
Project := foo

$ ./getopt_invalid_option.exe things
Project :=

$ ./getopt_invalid_option.exe --switch

raised GNAT.COMMAND_LINE.INVALID_SWITCH : Unrecognized option '--switch'

$ ./getopt_invalid_option.exe -sss

raised GNAT.COMMAND_LINE.INVALID_SWITCH : Unrecognized option '-s'

如何禁止上面的前两个示例?

我的情况有点复杂,因为我有一个部分,在此之后应该允许一切。通过阅读GNAT.Command_Line 的规范,我认为这可以通过在转到相应的部分之后调用Getopt("*") 来实现,但无论有没有一个部分,我都无法捕获无效开关(除非它们以'- ')。

在下面的代码中,我对与该部分相关的部分进行了注释;但关键是完整的解决方案也需要与该部分一起使用。

with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Command_Line; use GNAT.Command_Line;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Main is
   Project : Unbounded_String;
begin
   -- Initialize_Option_Scan (Section_Delimiters => "anything");
   -- Goto_Section ("");
   loop
      case Getopt ("-project= -help") is
         when '-' =>
            if Full_Switch = "-project" then
               Project := To_Unbounded_String (Parameter);
            elsif Full_Switch = "-help" then
               Put_Line ("Usage: etc... ");
               return;
            end if;
         when ASCII.Nul =>
            exit;
         when others =>
            Put_Line ("Error: unrecognized switch " & Full_Switch);
            return;
      end case;
   end loop;
   -- Goto_Section ("anything");
   -- loop
   --    exit when Getopt ("*") = ASCII.Nul;
   --    Put_Line ("Accepted: " & Full_Switch);
   -- end loop;
   Put_Line ("Project := " & To_String (Project));
end Main;

我尝试将第一个 Getopt 更改为:

      case Getopt ("-project= -help *") is

这根本不会改变行为!也试过了:

      case Getopt ("*") is

这一切都不允许,很奇怪。

【问题讨论】:

  • Getopt 移动到下一个开关,非开关需要 Get_Argument

标签: ada getopt


【解决方案1】:

我稍微修改了示例程序。以下似乎有效:

ma​​in.adb

with Ada.Text_IO;           use Ada.Text_IO;
with Ada.Exceptions;        use Ada.Exceptions;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with GNAT.Command_Line;     use GNAT.Command_Line;

procedure Main is
   
   Usage_Error : exception;
   
   -----------------------
   -- Raise_Usage_Error --
   -----------------------
   
   procedure Raise_Usage_Error (Switch : String) is
   begin
      raise Usage_Error with "error: unrecognized switch " & Switch;
   end Raise_Usage_Error;
   
   --------------------
   --  Display_Usage --
   --------------------   
   
   procedure Display_Usage is
   begin
      Put_Line ("usage: etc... ");
   end Display_Usage;
   
      
   Project : Unbounded_String;
   
begin
   
   Initialize_Option_Scan (Section_Delimiters => "anything");   
   
   --  Scan the first section.

   loop
      case Getopt ("* project= help") is
         
         when ASCII.Nul =>
            exit;            
         
         when 'p' =>
            if Full_Switch = "project" then
               Project := To_Unbounded_String (Parameter);
            else
               Raise_Usage_Error (Full_Switch);
            end if;
            
         when 'h' =>
            if Full_Switch = "help" then
               Display_Usage;
               return;
            else
               Raise_Usage_Error (Full_Switch);
            end if;            
        
         when others =>
            Raise_Usage_Error (Full_Switch);
            
      end case;
   end loop;
      
   Put ("OK: project = " & To_String (Project));
      
   --  Scan the "anything" section.

   Goto_Section ("anything");
   while Getopt ("*") /= ASCII.Nul loop
      Put (", arg = " & Full_Switch);
   end loop;    
   
exception
   when UE : Usage_Error =>
      Put_Line (Exception_Message (UE));
      --  Optional: Display_Usage
   
end Main;

test.sh

#!/bin/bash

function run_test ()
{
    echo "------------------------------"
    echo "  input   : $1"
    echo "  output  : $($1)"
}

run_test "./obj/main -project=foo"
run_test "./obj/main -project foo"
run_test "./obj/main -help"
run_test "./obj/main -project=foo -anything ddd eee fff"
run_test "./obj/main -help -anything ddd eee fff"
run_test "./obj/main -project=foo aaa bbb ccc"
run_test "./obj/main -help aaa bbb ccc"
run_test "./obj/main -project=foo aaa bbb ccc -anything ddd eee fff"
run_test "./obj/main -help aaa bbb ccc -anything ddd eee fff"
run_test "./obj/main --sss"
run_test "./obj/main -sss"

输出

$ ./test.sh 
------------------------------
  input     : ./obj/main -project=foo
  output    : OK: project = foo
------------------------------
  input     : ./obj/main -project foo
  output    : OK: project = foo
------------------------------
  input     : ./obj/main -help
  output    : usage: etc... 
------------------------------
  input     : ./obj/main -project=foo -anything ddd eee fff
  output    : OK: project = foo, arg = ddd, arg = eee, arg = fff
------------------------------
  input     : ./obj/main -help -anything ddd eee fff
  output    : usage: etc... 
------------------------------
  input     : ./obj/main -project=foo aaa bbb ccc
  output    : error: unrecognized switch aaa
------------------------------
  input     : ./obj/main -help aaa bbb ccc
  output    : usage: etc... 
------------------------------
  input     : ./obj/main -project=foo aaa bbb ccc -anything ddd eee fff
  output    : error: unrecognized switch aaa
------------------------------
  input     : ./obj/main -help aaa bbb ccc -anything ddd eee fff
  output    : usage: etc... 
------------------------------
  input     : ./obj/main --sss
  output    : error: unrecognized switch --sss
------------------------------
  input     : ./obj/main -sss
  output    : error: unrecognized switch -sss

【讨论】:

    最近更新 更多