2010-08-06 11 views
34

Ich habe gerade ein Konsolen-Dienstprogramm geschrieben und entschieden, NDesk.Options für das Kommandozeilen-Parsing zu verwenden. Meine Frage ist, wie erzwinge ich die erforderlichen Befehlszeilenoptionen?Wie erzwingen erforderlichen Befehlszeilenoptionen mit NDesk.Options?

Ich sehe im docs dass:

Optionen mit einem Sollwert (anhängen ‚=‘ dem Optionsnamen) oder einen optionalen Wert (anhängen ‚:‘ dem Optionsnamen).

Wenn ich jedoch einen = am Ende des Optionsnamens setzen, gibt es keinen Unterschied im Verhalten. Im Idealfall würde die Parse-Methode eine Ausnahme auslösen.

Gibt es noch etwas, das ich tun muss?

Hier ist mein Testcode:

class Program 
{ 
    static void Main(string[] args) 
    { 
     bool show_help = false; 
     string someoption = null; 

     var p = new OptionSet() { 
      { "someoption=", "Some String Option", v => someoption = v}, 
      { "h|help", "show this message and exit", v => show_help = v != null } 
     }; 

     List<string> extra; 
     try 
     { 
      extra = p.Parse(args); 
     } 
     catch (OptionException e) 
     { 
      System.Console.Write("myconsole: "); 
      System.Console.WriteLine(e.Message); 
      System.Console.WriteLine("Try `myconsole --help' for more information."); 
      return; 
     } 

     if (show_help) 
     { 
      ShowHelp(p); 
      return; 
     } 

     System.Console.WriteLine("=================="); 
     System.Console.WriteLine(someoption); 
    } 

    static void ShowHelp(OptionSet p) 
    { 
     System.Console.WriteLine("Usage: myconsole [OPTIONS]"); 
     System.Console.WriteLine(); 
     System.Console.WriteLine("Options:"); 
     p.WriteOptionDescriptions(System.Console.Out); 
    } 
} 
+1

Ich habe das gleiche Problem. Seams NDesk.Options wird keine Ausnahme auslösen. –

Antwort

39

Das Problem ist, dass die Dokumentation ist nicht so klar, wie es offensichtlich sein muss. :-(

Insbesondere Stand:

http://www.ndesk.org/doc/ndesk-options/NDesk.Options/OptionValueType.html#F:NDesk.Options.OptionValueType.Required

Die = innerhalb einer Option-Spezifikation an die OptionSet als Ganzes nicht anwendbar ist, sondern nur auf den Wert für die jeweilige Option.

die Bedeutung dieser ist wirklich nur in zwei Szenarien relevant, also lassen Sie uns zunächst die OptionSet Parser betrachten:

string a = null; 
string b = null; 
var options = new OptionSet { 
    { "a=", v => a = v }, 
    { "b=", v => b = v }, 
}; 

Szenario 1, wo es wichtig ist, dass OptionSet.Parse() arbeitet in einem Single-Pass, Forward-only-Methode, und nicht betrachten Option Werte zu bestimmen, ob sie "sollten" Werte sein. So betrachtet:

options.Parse(new[]{"-a", "-b"}); 

Das Ergebnis davon wird sein, dass a den Wert "-b" und bnull ist. Da der Handler für -aeinen Wert erfordert, erhält er immer erhält den folgenden Wert (es sei denn, der Wert ist in die ursprüngliche Option "codiert", beispielsweise -a=value).

Der zweite Ort, wo dies wichtig ist, wenn ein Wert erfordernden Option die letzte Option ist, und es ist kein Wert vorhanden für sie:

options.Parse(new[]{"-a"}); 

Dies wird eine OptionException, als Handler werfen für -aerfordert einen Wert, und kein Wert ist vorhanden.

Wenn Sie also eine Option, dass selbst erforderlich ist (im Gegensatz zu einer Option im Gegensatz, der einen Wert erfordert), müssen Sie manuell für diese überprüfen:

string dir = null; 
new OptionSet { 
    { "o=", v => dir = v }, 
}.Parse (args); 

if (dir == null) 
    throw new InvalidOperationException ("Missing required option -o=DIR"); 
2

One NDesk.Options ein wenig verlängern Bit, um diese Funktionalität hinzuzufügen.

zunächst eine SetupOption Klasse erstellen, die INotifyPropertyChanged implementieren würde:

class SetupOption<T> : INotifyPropertyChanged 
{ 
    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 

    #endregion 

    private T _value; 

    public T Value 
    { 
     get 
     { 
      return _value; 
     } 
     set 
     { 
      _value = value; 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(_value, new PropertyChangedEventArgs("Value")); 
      } 
     } 
    } 
} 

Zweitens fügen Sie eine Überlastung zu ActionOption, die eine Instanz von INotifyPropertyChanged als Argument (nennen wir es Target).

Drittens ändern Sie die Option-Klasse, um private INotifyPropertyChanged targetValue und private bool optionSet hinzuzufügen.

Viertens übergeben Sie den targetValue beim Erstellen an die Option. Abonnieren Sie das PropertyChanged-Ereignis. Setzen Sie in diesem Fall "optionSet" auf "true", wenn der Absender nicht null ist.

Fügen Sie der Option-Klasse eine Validate() - Methode hinzu, die eine Ausnahme auslöst, wenn targetValue nicht null und optionSet false ist.

Schließlich fügen Sie dem OptionContext eine Validate() - Methode hinzu, die alle Optionen durchlaufen und ihre entsprechenden Validate() -Methoden aufrufen würde. Rufen Sie es am Ende der Parse() -Methode auf. Hier

ist der Reißverschluss des modifizierten Code: http://www.davidair.com/misc/options.zip