2008-09-10 9 views
38

Wenn Sie ein Programm schreiben, das über die Befehlszeile ausführbar ist, möchten Sie dem Benutzer häufig mehrere Optionen oder Flags und möglicherweise mehr als ein Argument anbieten. Ich bin viele Male durch diesen Weg gestolpert, aber gibt es eine Art Designmuster für das Durchlaufen von Argumenten und das Ausdrehen der entsprechenden Funktionen?Was ist das Entwurfsmuster für die Verarbeitung von Befehlszeilenargumenten?

Bedenken Sie:

myprogram -f filename -d directory -r regex 

Wie organisieren Sie den Code, nachdem Sie die Argumente verwenden Sie das eingebaute Funktionen für Ihre Sprache abrufen? (sprachspezifische Antworten erwünscht, wenn das hilft, eine Antwort zu artikulieren)

+0

Dies sollte als "sprachunabhängig" markiert werden, da die Frage sowohl nach einem Entwurfsmuster als auch nach Vorschlägen fragt. – martinatime

Antwort

13

Ich kenne keine dokumentierten "Muster" für die Verarbeitung.

Ich glaube, eine der ältesten Bibliotheken/APIs für die Behandlung von Argumenten ist getopt. Googlen "getopt" zeigt viele man-Seiten und Links zu Implementierungen.

Im Allgemeinen habe ich einen Einstellungsdienst in meiner Anwendung, mit dem der Argumentprozessor kommunizieren kann. Argumente werden dann in etwas in diesen Service übersetzt, das die Anwendung dann abfragt. Dies könnte so einfach wie ein Wörterbuch von Einstellungen sein (wie eine String-Einstellung namens "Dateiname").

1

Ich würde empfehlen, eine Befehlszeilenprozessor-Bibliothek zu verwenden. Some Russian guy erstellt eine anständige, aber es gibt Tonnen von ihnen da draußen. Wir sparen Ihnen Zeit, damit Sie sich auf den Zweck Ihrer App konzentrieren können, anstatt Befehlszeilenparameter zu analysieren!

+0

Ich mag diesen Artikel. Vielen Dank! :-) – craigmoliver

5

Sie haben die Sprache nicht erwähnt, aber für Java haben wir Apache Commons CLI geliebt. Für C/C++, getopt.

0

Ich benutze die Getopts::std und Getopts::long in Perl und auch die Getopt Funktion in C. Diese standardisiert die Analyse und das Format der Parameter. Andere Sprachen haben unterschiedliche Mechanismen, um mit diesen umzugehen.

this helps

0

Die Standardausführung in der Regel folgt, was getopt der Fall ist, gibt es getopt Bibliotheken für viele Sprachen, .NET, Python, C, Perl, PHP, usw.

Das grundlegende Design ist habe einen Kommandozeilen-Parser, der Teil für Teil die Argumente zurückgibt, die in einer Schleife überprüft werden.

This Artikel diskutiert es in etwas mehr Details.

2

Die boost::program_options Bibliothek ist schön, wenn man in C++ sind und haben den Luxus-Boost verwenden.

+0

Es ist eine gute Wahl. Diese Bibliothek ist ein wenig komplex im Vergleich zum alten getopt, erlaubt aber auch die Verwendung von Konfigurationsdateien (Ersetzen oder Integrieren von Befehlszeilenargumenten). –

0

Ich bin nicht so sehr an den Bibliotheken interessiert, obwohl das definitiv hilfreich ist.Ich habe mehr nach einem "Pseudo-Code" gesucht, der die Verarbeitung von beispielsweise einem durchschnittlichen Bündel von Flags und einer Reihe längerer Argumente veranschaulicht.

+0

Vielleicht möchten Sie dies in Ihrer Frage und nicht hier zur Klärung aktualisieren. Es ist nur für Antworten gedacht. – rpattabi

2

Angenommen, Sie haben ein "config" -Objekt, das Sie mit den Flags und einem geeigneten Befehlszeilen-Parser einrichten wollen, der die Syntax der Befehlszeile analysiert und einen konstanten Strom der Optionen liefert, hier ein Pseudocode-Block

while (current_argument = cli_parser_next()) { 
    switch(current_argument) { 
     case "f": //Parser strips the dashes 
     case "force": 
      config->force = true; 
      break; 
     case "d": 
     case "delete": 
      config->delete = true; 
      break; 
     //So on and so forth 
     default: 
      printUsage(); 
      exit; 
    } 
} 
2

Ich bevorzuge Optionen wie "-t Text" und "-i 44"; Ich mag nicht "-fname" oder "--very-long-argument = some_value".

Und "-?", "-h" und "/ h" erzeugen alle einen Hilfebildschirm.

Hier ist, wie mein Code aussieht:

int main (int argc, char *argv[]) 
    { int i; 
     char *Arg; 
     int ParamX, ParamY; 
     char *Text, *Primary; 

    // Initialize... 
    ParamX = 1; 
    ParamY = 0; 
    Text = NULL; 
    Primary = NULL; 

    // For each argument... 
    for (i = 0; i < argc; i++) 
     { 
     // Get the next argument and see what it is 
     Arg = argv[i]; 
     switch (Arg[0]) 
     { 
     case '-': 
     case '/': 
      // It's an argument; which one? 
      switch (Arg[1]) 
       { 
       case '?': 
       case 'h': 
       case 'H': 
        // A cry for help 
        printf ("Usage: whatever...\n\n"); 
        return (0); 
        break; 

       case 't': 
       case 'T': 
        // Param T requires a value; is it there? 
        i++; 
        if (i >= argc) 
        { 
        printf ("Error: missing value after '%s'.\n\n", Arg); 
        return (1); 
        } 

        // Just remember this 
        Text = Arg; 

        break; 

       case 'x': 
       case 'X': 
        // Param X requires a value; is it there? 
        i++; 
        if (i >= argc) 
        { 
        printf ("Error: missing value after '%s'.\n\n", Arg); 
        return (1); 
        } 

        // The value is there; get it and convert it to an int (1..10) 
        Arg = argv[i]; 
        ParamX = atoi (Arg); 
        if ((ParamX == 0) || (ParamX > 10)) 
        { 
        printf ("Error: invalid value for '%s'; must be between 1 and 10.\n\n", Arg); 
        return (1); 
        } 

        break; 

       case 'y': 
       case 'Y': 
        // Param Y doesn't expect a value after it 
        ParamY = 1; 
        break; 

       default: 
        // Unexpected argument 
        printf ("Error: unexpected parameter '%s'; type 'command -?' for help.\n\n", Arg); 
        return (1); 
        break; 
       } 

      break; 

     default: 
      // It's not a switch that begins with '-' or '/', so it's the primary option 
      Primary = Arg; 

      break; 
     } 
     } 

    // Done 
    return (0); 
    } 
4

Ein paar Kommentare zu diesem ...

Erstens, während es keine Muster sind per se, im Wesentlichen eine mechanische Bewegung ist es, einen Parser zu schreiben, da eine Grammatik gegeben ist, kann ein Parser leicht erzeugt werden. Werkzeuge wie Bison und ANTLR kommen mir in den Sinn.

Das gesagt, Parser Generatoren sind in der Regel Overkill für die Befehlszeile. Das übliche Muster ist also, ein paar Male selbst zu schreiben (wie andere gezeigt haben), bis es Ihnen langweilig wird, sich mit den langweiligen Details zu befassen und eine Bibliothek zu finden, die es für Sie erledigt.

schrieb ich eine für C++, die eine Reihe von Aufwand spart, die imparts getopt und macht schöne Verwendung von Vorlagen: TCLAP

+0

'' TCLAP'' ist eine fantastische CLI-Parsing-Bibliothek. Das Vorparse-Setup von Regeln und das Post-Parse-Extrahieren des Parsing von "argv" ist sehr nützlich, intuitiv und hilft dabei, das Programm in die korrekten diskreten Komponenten (IMHO) zu zerlegen. – Sean

2

ich von mes5k auf der ANTLR Antwort bin Riffing. Diese link to Codeproject ist für einen Artikel, der ANLTR diskutiert und das Besuchsmuster verwendet, um die Aktionen zu implementieren, die Sie von Ihrer App ausführen möchten. Es ist gut geschrieben und es lohnt sich, es zu überprüfen.

12

ich denke, die folgende Antwort ist mehr entlang den Linien von dem, was Sie suchen:

Sie die Vorlage Muster (Template-Methode in „Design Patterns“ [Gamma, el al]) aussehen sollte

bei Anwendung

kurz gesagt, es ist insgesamt die Verarbeitung wie folgt aussieht:

If the arguments to the program are valid then 
    Do necessary pre-processing 
    For every line in the input 
     Do necessary input processing 
    Do necessary post-processing 
Otherwise 
    Show the user a friendly usage message 

kurz gesagt, eine ConsoleEngineBase Klasse implementieren, die Methoden zur hat:

PreProcess() 
ProcessLine() 
PostProcess() 
Usage() 
Main() 

Erstellen Sie dann ein Gehäuse, das eine ConsoleEngine() - Instanz instanziiert und die Main() - Nachricht sendet, um es zu starten.

Um ein gutes Beispiel dafür, wie dies zu einer Konsole oder Kommandozeilen-Programm anzuwenden, um den folgenden Link finden Sie unter: http://msdn.microsoft.com/en-us/magazine/cc164014.aspx

Das Beispiel ist in C#, aber die Ideen in einer anderen Umgebung leicht umgesetzt werden.

Sie würden die GetOpt() nur als den Teil betrachten, der in die Argumentbehandlung (Vorverarbeitung) passt.

Hoffe, das hilft.

+1

upvoted für das Festhalten an den Konzepten statt Implementierung. Ich denke, das sollte die gewählte Antwort sein. – Plasmarob

3

Nun, es ist ein alter Post, aber ich möchte noch dazu beitragen.Die Frage war auf die Wahl der Entwurfsmuster gerichtet, aber ich konnte eine Menge Diskussion darüber sehen, welche Bibliothek zu verwenden ist. Ich habe Microsoft-Link nach Lindsay ausgecheckt, die über Template-Design-Muster spricht zu verwenden.

Allerdings bin ich nicht mit der Post überzeugt. Die Absicht des Vorlagenmusters besteht darin, eine Vorlage zu definieren, die von verschiedenen anderen Klassen implementiert wird, um ein einheitliches Verhalten zu erzielen. Ich glaube nicht, dass das Parsen der Befehlszeile darin passt.

Ich würde lieber mit "Command" Design-Muster gehen. Dieses Muster eignet sich am besten für menügesteuerte Optionen.

http://www.blackwasp.co.uk/Command.aspx

so in Ihrem Fall, -f -d und -r alle Befehle, die wird definiert gemeinsamen oder getrennten Empfänger. So können zukünftig mehr Empfänger definiert werden. Der nächste Schritt wird sein, diese Zuständigkeiten von Befehlen zu verketten, falls eine Verarbeitungskette benötigt wird. Für die ich wählen würde.

http://www.blackwasp.co.uk/ChainOfResponsibility.aspx

Ich denke, die Kombination dieser beiden sind am besten den Code für Befehlszeile Verarbeitung oder einem menügesteuerten Ansatz zu organisieren.

Verwandte Themen