Лучший способ разобрать аргументы командной строки в C #? [закрыто]


731

При создании консольных приложений, которые принимают параметры, вы можете использовать переданные аргументы Main(string[] args).

В прошлом я просто индексировал / зацикливал этот массив и делал несколько регулярных выражений для извлечения значений. Однако, когда команды становятся более сложными, анализ может стать довольно уродливым.

Итак, я заинтересован в:

  • Библиотеки, которые вы используете
  • Шаблоны, которые вы используете

Предположим, что команды всегда придерживаются общих стандартов, таких как ответы здесь .


Предыдущее обсуждение, содержащее разделенную строку-параметры-командной строки-в-строку-в-c # , может иметь некоторые ответы.
Гимел

1
Привет, извините, это немного не по теме. однако я использую «Настройки приложения» для передачи аргумента в приложение. Я обнаружил, что его довольно просто использовать, и не нужно писать разбор аргументов / файлов, и нет необходимости в дополнительной библиотеке. msdn.microsoft.com/en-us/library/aa730869(VS.80).aspx
зовите меня Стив

44
@ Позвоните мне, Стив: аргументы командной строки в том, что они могут варьироваться в зависимости от вызова - как это сделать с настройками приложения?
reinierpost

Ответы:


324

Я настоятельно рекомендую использовать NDesk.Options ( Документация ) и / или Mono.Options (один и тот же API, другое пространство имен). Пример из документации :

bool show_help = false;
List<string> names = new List<string> ();
int repeat = 1;

var p = new OptionSet () {
    { "n|name=", "the {NAME} of someone to greet.",
       v => names.Add (v) },
    { "r|repeat=", 
       "the number of {TIMES} to repeat the greeting.\n" + 
          "this must be an integer.",
        (int v) => repeat = v },
    { "v", "increase debug message verbosity",
       v => { if (v != null) ++verbosity; } },
    { "h|help",  "show this message and exit", 
       v => show_help = v != null },
};

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

14
NDesk.options великолепен, но, похоже, на самом деле не поддерживает консольные приложения с помощью более чем одной отдельной команды. Если вы хотите этого, попробуйте ManyConsole, который основан на NDesk.Options: nuget.org/List/Packages/ManyConsole
Фрэнк Швитерман,

5
Когда у меня есть одно приложение с несколькими различными командами, я «накладываю» наборы параметров. Возьмите mdoc ( docs.go-mono.com/index.aspx?link=man%3amdoc%281%29 ), который имеет «глобальный» OptionSet ( github.com/mono/mono/blob/master/mcs/tools/ mdoc / ... ) , который делегирует в OptionSet каждой команды (например , github.com/mono/mono/blob/master/mcs/tools/mdoc/... )
jonp

3
У меня нет работы. Может читать целочисленные аргументы нормально, но не строки. Переменные продолжают получать аргументы (например, «s», «a» и т. Д.) Вместо значений аргументов (например, «serverName», «ApplicationName»). Сказал и использовал «Библиотеку анализатора командной строки». Хорошо, пока.
Джей

2
@AshleyHenderson Во-первых, он маленький и гибкий. Большинство решений работают только с необязательными именованными аргументами (т. Е. Не могут делать подобное git checkout master), или их аргументы не являются гибкими (т. Е. Не поддерживают --foo 123= --foo=123= -f 123=, -f=123а также -v -h= -vh).
Wernight

1
@FrankSchwieterman, это должен быть собственный ответ. И спасибо за совет, ManyConsole - настоящее удовольствие, идеально подходит для меня.
Квентин Старин

197

Мне очень нравится библиотека анализатора командной строки ( http://commandline.codeplex.com/ ). У него очень простой и элегантный способ настройки параметров с помощью атрибутов:

class Options
{
    [Option("i", "input", Required = true, HelpText = "Input file to read.")]
    public string InputFile { get; set; }

    [Option(null, "length", HelpText = "The maximum number of bytes to process.")]
    public int MaximumLenght { get; set; }

    [Option("v", null, HelpText = "Print details during execution.")]
    public bool Verbose { get; set; }

    [HelpOption(HelpText = "Display this help screen.")]
    public string GetUsage()
    {
        var usage = new StringBuilder();
        usage.AppendLine("Quickstart Application 1.0");
        usage.AppendLine("Read user manual for usage instructions...");
        return usage.ToString();
    }
}

6
Это библиотека, на которой я остановился. Я пишу приложения для большой компании, которые нужно обслуживать в течение многих лет - эта библиотека постоянно обновляется с 2005 года, кажется популярной, написана людьми, активно работающими в сообществе C #, и лицензируется в стиле BSD на случай, если поддержка исчезает.
Чарльз Бернс

Я рекомендую это также. Моя единственная проблема заключалась в том, что: Задание разрешенной комбинации аргументов (например, если аргумент move должен иметь аргументы source и dest) может быть возможным для атрибутов. Но вам, может быть, лучше сделать это с помощью отдельной логики проверки аргументов
Линдон Уайт

1
Мне нравится класс Options. Кажется, он поддерживает также безымянные параметры и флаги --recursive.
Wernight

2
Я только что проверил это, и я реализовал опцию для моего приложения всего за несколько минут. Использовать библиотеку чрезвычайно просто.
Трисмегист

3
Я нашел эту библиотеку очень строгой для себя. Если вам нужны эксклюзивные наборы, вы не можете определить необходимые параметры для каждого набора, поэтому необходимо проверить их вручную. Вы не можете определить минимальное требование для неназванных значений, вы также должны проверить их вручную. Помощь строителя экрана тоже не гибкая вообще. Если поведение библиотеки не соответствует вашим потребностям сразу после установки, вы практически ничего не можете изменить.
Сергей Коструков

50

Библиотека WPA TestApi поставляется с одним из лучших синтаксических анализаторов командной строки для разработки на C #. Я настоятельно рекомендую изучить это из блога Иво Манолова об API :

// EXAMPLE #2:
// Sample for parsing the following command-line:
// Test.exe /verbose /runId=10
// This sample declares a class in which the strongly-
// typed arguments are populated
public class CommandLineArguments
{
   bool? Verbose { get; set; }
   int? RunId { get; set; }
}

CommandLineArguments a = new CommandLineArguments();
CommandLineParser.ParseArguments(args, a);

19
+1. Синтаксический анализ командной строки - это то, что действительно должно исходить от поставщика (например, Microsoft), а не через сторонний инструмент, даже если поддержка поставщика идет в обход.
Джоэл Кохорн

2
Тем не менее, принятый ответ (моно) является следующей лучшей вещью.
Джоэл Коухорн

6
@Joel, какая часть важна для анализа командной строки от поставщика? Каковы ваши причины?
Greenoldman

3
@marcias: я думаю, он имел в виду, что, вероятно, это должно было быть из коробки ... как и многие другие вещи :)
user7116

Библиотека огромна! Содержит гораздо больше, чем мне нужно ...
Рири

24

2
У параметров NDesk очень хороший API
user35149

2
Я добавлю еще один голос за NDesk, он работает хорошо, ненавязчив и хорошо документирован.
Теренс

1
Mono.GetOptions очень старый, NDesk.Options гораздо лучше (или Mono.Options, если вы предпочитаете, его тот же класс, здесь: anonsvn.mono-project.com/source/trunk/mcs/class/Mono.Options/… )
Мэтт Энрайт

7
@ Адам Орен: мой ответ 1 год и 1 месяц! структура моноблока была переработана. Этот код теперь помещен в anonsvn.mono-project.com/viewvc/branches/mono-2-2/mcs/class/…
abatishchev

6
@Tormod: это Mono.GetOptions, который устарел, а не Mono.Options. Mono.Options по-прежнему поддерживается.
jonp

14

Похоже, у каждого есть свои любимые парсеры командной строки, думаю, мне лучше добавить и мой :).

http://bizark.codeplex.com/

Эта библиотека содержит анализатор командной строки , который инициализирует класс значениями из командной строки. У этого есть тонна особенностей (я строил это в течение многих лет).

Из документации ...

Синтаксический анализ командной строки в среде BizArk имеет следующие ключевые особенности:

  • Автоматическая инициализация: свойства класса автоматически устанавливаются на основе аргументов командной строки.
  • Свойства по умолчанию: отправить значение без указания имени свойства.
  • Преобразование значений. Использует мощный класс ConvertEx, также включенный в BizArk, для преобразования значений в правильный тип.
  • Булевы флаги: Флаги можно указать, просто используя аргумент (например, / b для true и / b- для false) или добавив значение true / false, да / нет и т. Д.
  • Массивы аргументов: просто добавьте несколько значений после имени командной строки, чтобы задать свойство, определяемое как массив. Например, / x 1 2 3 заполнит x массивом {1, 2, 3} (при условии, что x определен как массив целых чисел).
  • Псевдонимы командной строки: свойство может поддерживать несколько псевдонимов командной строки для него. Например, справка использует псевдоним?
  • Частичное распознавание имени: вам не нужно указывать полное имя или псевдоним, достаточно только написания, чтобы синтаксический анализатор устранял неоднозначность свойства / псевдонима от других.
  • Поддерживает ClickOnce. Может инициализировать свойства, даже если они указаны в виде строки запроса в URL для развернутых приложений ClickOnce. Метод инициализации командной строки обнаружит, работает ли он как ClickOnce или нет, поэтому ваш код не должен изменяться при его использовании.
  • Автоматически создает /? help: Это включает в себя хорошее форматирование, которое учитывает ширину консоли.
  • Загрузка / сохранение аргументов командной строки в файл: это особенно полезно, если у вас есть несколько больших, сложных наборов аргументов командной строки, которые вы хотите запустить несколько раз.

2
Я обнаружил, что анализатор командной строки BizArk гораздо проще и лучше, чем другие. Настоятельно рекомендуется!
Борис Модылевский


9

CLAP (анализатор аргументов командной строки) имеет удобный API и прекрасно документирован. Вы делаете метод, комментируя параметры. https://github.com/adrianaisemberg/CLAP


2
Это очень просто в использовании, и их сайт качается. Однако их синтаксис не очень интуитивно понятен: myapp myverb -argname argvalue(должен иметь -argname) или myapp -help(обычно --help).
Wernight

@ Каждую ночь вы можете использовать параметр IsDefault на Глаголе, чтобы его можно было опустить. Я не нашел поддержки для позиционных параметров, но я использовал только позиционные параметры, когда сам анализировал командную строку. Гораздо понятнее использовать именованные аргументы, за которыми следуют значения IMHO.
Loudenvier

5

Есть множество решений этой проблемы. Для полноты и предоставления альтернативы, если кто-то захочет, я добавляю этот ответ для двух полезных классов в моей библиотеке кода Google. .

Первый - это ArgumentList, который отвечает только за анализ параметров командной строки. Он собирает пары «имя-значение», определенные переключателями «/ x: y» или «-x = y», а также собирает список «неназванных» записей. Это основное использование обсуждается здесь , посмотреть класс здесь .

Вторая часть этого - CommandInterpreter, который создает полнофункциональное приложение командной строки из вашего .Net-класса. Например:

using CSharpTest.Net.Commands;
static class Program
{
    static void Main(string[] args)
    {
        new CommandInterpreter(new Commands()).Run(args);
    }
    //example ‘Commands’ class:
    class Commands
    {
        public int SomeValue { get; set; }
        public void DoSomething(string svalue, int ivalue)
        { ... }

С помощью приведенного выше примера кода вы можете выполнить следующее:

Program.exe DoSomething "строковое значение" 5

-- или --

Program.exe dosomething / ivalue = 5 -svalue: «строковое значение»

Это так просто или так сложно, как вам нужно. Вы можете просмотреть исходный код , просмотреть справку или загрузить двоичный файл .


4

Мне нравится этот , потому что вы можете «определить правила» для аргументов, необходимых или нет, ...

или если вы парень из Unix, то вам может понравиться порт GNU Getopt .NET .


4

Тебе может понравиться мой Rug.Cmd

Простой в использовании и расширяемый парсер аргументов командной строки. Ручки: Bool, Plus / Minus, String, String List, CSV, Перечисление.

Встроенный в /? справочный режим.

Построен в '/ ??' и '/? D' режимы генератора документов.

static void Main(string[] args) 
{            
    // create the argument parser
    ArgumentParser parser = new ArgumentParser("ArgumentExample", "Example of argument parsing");

    // create the argument for a string
    StringArgument StringArg = new StringArgument("String", "Example string argument", "This argument demonstrates string arguments");

    // add the argument to the parser 
    parser.Add("/", "String", StringArg);

    // parse arguemnts
    parser.Parse(args);

    // did the parser detect a /? argument 
    if (parser.HelpMode == false) 
    {
        // was the string argument defined 
        if (StringArg.Defined == true)
        {
            // write its value
            RC.WriteLine("String argument was defined");
            RC.WriteLine(StringArg.Value);
        }
    }
}

Редактировать: это мой проект, и поэтому этот ответ не должен рассматриваться как одобрение третьей стороны. Тем не менее, я использую его для каждой программы на основе командной строки, которую я пишу, это с открытым исходным кодом, и я надеюсь, что другие могут извлечь из этого пользу.


Просто к сведению, что вы должны добавить небольшой отказ от ответственности, что вы связаны с проектом Rug.Cmd (как упомянуто в FAQ): stackoverflow.com/faq#promotion - Ничего страшного, так как вы продвигаете открытый исходный проект, но все же хорошо добавить отказ от ответственности;) +1 кстати ... выглядит довольно хорошо сделано.
Джейсон Даун

Спасибо за указание на это и спасибо за +1, я сделаю все возможное, чтобы я был более откровенным о моей принадлежности.
Фил Тью

Не беспокойтесь ... Есть некоторые сторонники такого типа вещей (я не один из них), поэтому я хотел бы дать людям голову. Опять же, обычно это не проблема для проектов с открытым исходным кодом. В основном это мешает людям рассылать спам-рекомендации по своим (платным) продуктам.
Джейсон Даун

3

Парсер аргументов командной строки находится по адресу http://www.codeplex.com/commonlibrarynet

Он может анализировать аргументы, используя
1. атрибуты
2. явные вызовы
3. одну строку из нескольких аргументов ИЛИ строковый массив

Он может обрабатывать такие вещи, как следующие:

- config : Qa - начальная дата : $ { today } - регион : 'Нью-Йорк' Настройки01

Это очень просто в использовании.


2

Это обработчик, который я написал на основе Novell Options класса .

Этот предназначен для консольных приложений, которые выполняют while (input !="exit") цикл стилей, например, интерактивной консоли, такой как FTP-консоль.

Пример использования:

static void Main(string[] args)
{
    // Setup
    CommandHandler handler = new CommandHandler();
    CommandOptions options = new CommandOptions();

    // Add some commands. Use the v syntax for passing arguments
    options.Add("show", handler.Show)
        .Add("connect", v => handler.Connect(v))
        .Add("dir", handler.Dir);

    // Read lines
    System.Console.Write(">");
    string input = System.Console.ReadLine();

    while (input != "quit" && input != "exit")
    {
        if (input == "cls" || input == "clear")
        {
            System.Console.Clear();
        }
        else
        {
            if (!string.IsNullOrEmpty(input))
            {
                if (options.Parse(input))
                {
                    System.Console.WriteLine(handler.OutputMessage);
                }
                else
                {
                    System.Console.WriteLine("I didn't understand that command");
                }

            }

        }

        System.Console.Write(">");
        input = System.Console.ReadLine();
    }
}

И источник:

/// <summary>
/// A class for parsing commands inside a tool. Based on Novell Options class (http://www.ndesk.org/Options).
/// </summary>
public class CommandOptions
{
    private Dictionary<string, Action<string[]>> _actions;
    private Dictionary<string, Action> _actionsNoParams;

    /// <summary>
    /// Initializes a new instance of the <see cref="CommandOptions"/> class.
    /// </summary>
    public CommandOptions()
    {
        _actions = new Dictionary<string, Action<string[]>>();
        _actionsNoParams = new Dictionary<string, Action>();
    }

    /// <summary>
    /// Adds a command option and an action to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action action)
    {
        _actionsNoParams.Add(name, action);
        return this;
    }

    /// <summary>
    /// Adds a command option and an action (with parameter) to perform when the command is found.
    /// </summary>
    /// <param name="name">The name of the command.</param>
    /// <param name="action">An action delegate that has one parameter - string[] args.</param>
    /// <returns>The current CommandOptions instance.</returns>
    public CommandOptions Add(string name, Action<string[]> action)
    {
        _actions.Add(name, action);
        return this;
    }

    /// <summary>
    /// Parses the text command and calls any actions associated with the command.
    /// </summary>
    /// <param name="command">The text command, e.g "show databases"</param>
    public bool Parse(string command)
    {
        if (command.IndexOf(" ") == -1)
        {
            // No params
            foreach (string key in _actionsNoParams.Keys)
            {
                if (command == key)
                {
                    _actionsNoParams[key].Invoke();
                    return true;
                }
            }
        }
        else
        {
            // Params
            foreach (string key in _actions.Keys)
            {
                if (command.StartsWith(key) && command.Length > key.Length)
                {

                    string options = command.Substring(key.Length);
                    options = options.Trim();
                    string[] parts = options.Split(' ');
                    _actions[key].Invoke(parts);
                    return true;
                }
            }
        }

        return false;
    }
}

2

Мой личный фаворит - http://www.codeproject.com/KB/recipes/plossum_commandline.aspx Питера Палотаса:

[CommandLineManager(ApplicationName="Hello World",
    Copyright="Copyright (c) Peter Palotas")]
class Options
{
   [CommandLineOption(Description="Displays this help text")]
   public bool Help = false;

   [CommandLineOption(Description = "Specifies the input file", MinOccurs=1)]
   public string Name
   {
      get { return mName; }
      set
      {
         if (String.IsNullOrEmpty(value))
            throw new InvalidOptionValueException(
                "The name must not be empty", false);
         mName = value;
      }
   }

   private string mName;
}

2

Недавно я наткнулся на реализацию синтаксического анализа командной строки FubuCore, которая мне действительно нравится по следующим причинам:

  • он прост в использовании - хотя я не смог найти документацию для него, решение FubuCore также предоставляет проект, содержащий хороший набор модульных тестов, которые говорят о функциональности больше, чем любая документация
  • у него приятный объектно-ориентированный дизайн, нет повторения кода или других подобных вещей, которые я имел в своих приложениях для разбора командной строки
  • это декларативно: вы в основном пишете классы для команд и наборов параметров и украшаете их атрибутами, чтобы установить различные опции (например, имя, описание, обязательный / необязательный)
  • библиотека даже печатает хороший график использования, основанный на этих определениях

Ниже приведен простой пример того, как это использовать. Чтобы проиллюстрировать использование, я написал простую утилиту, которая имеет две команды: - add (добавляет объект в список - объект состоит из имени (строки), значения (int) и логического флага) - list (lists все добавленные в данный момент объекты)

Прежде всего, я написал класс Command для команды add:

[Usage("add", "Adds an object to the list")]
[CommandDescription("Add object", Name = "add")]
public class AddCommand : FubuCommand<CommandInput>
{
    public override bool Execute(CommandInput input)
    {
        State.Objects.Add(input); // add the new object to an in-memory collection

        return true;
    }
}

Эта команда принимает экземпляр CommandInput в качестве параметра, поэтому я определю это следующим образом:

public class CommandInput
{
    [RequiredUsage("add"), Description("The name of the object to add")]
    public string ObjectName { get; set; }

    [ValidUsage("add")]
    [Description("The value of the object to add")]
    public int ObjectValue { get; set; }

    [Description("Multiply the value by -1")]
    [ValidUsage("add")]
    [FlagAlias("nv")]
    public bool NegateValueFlag { get; set; }
}

Следующая команда - это список, который реализован следующим образом:

[Usage("list", "List the objects we have so far")]
[CommandDescription("List objects", Name = "list")]
public class ListCommand : FubuCommand<NullInput>
{
    public override bool Execute(NullInput input)
    {
        State.Objects.ForEach(Console.WriteLine);

        return false;
    }
}

Команда 'list' не принимает параметров, поэтому для этого я определил класс NullInput:

public class NullInput { }

Теперь осталось только подключить это в методе Main (), например так:

    static void Main(string[] args)
    {
        var factory = new CommandFactory();
        factory.RegisterCommands(typeof(Program).Assembly);

        var executor = new CommandExecutor(factory);

        executor.Execute(args);
    }

Программа работает должным образом, выводит подсказки о правильном использовании в случае, если какие-либо команды недопустимы:

  ------------------------
    Available commands:
  ------------------------
     add -> Add object
    list -> List objects
  ------------------------

И пример использования для команды «add»:

Usages for 'add' (Add object)
  add <objectname> [-nv]

  -------------------------------------------------
    Arguments
  -------------------------------------------------
     objectname -> The name of the object to add
    objectvalue -> The value of the object to add
  -------------------------------------------------

  -------------------------------------
    Flags
  -------------------------------------
    [-nv] -> Multiply the value by -1
  -------------------------------------

2

Командлеты Powershell.

Разбор выполняется с помощью powershell на основе атрибутов, указанных в командлетах, поддержки проверок, наборов параметров, конвейеризации, отчетов об ошибках, справки и, что лучше всего, возврата объектов .NET для использования в других командлетах.

Несколько ссылок, которые я нашел полезными для начала:


2

C # CLI - очень простая библиотека синтаксического анализа аргументов командной строки, которую я написал. Это хорошо документировано и с открытым исходным кодом.


Хорошо документированы? Где находится документация?
Suhas

Существует внутренняя документация (т.е. в базе кода), а также внешняя документация (см. Readme.mkdФайл в Documentationпапке).
Бернард

Хорошо, я поспешно прокомментировал. Возможно, вы могли бы переместить свой проект на github, и ваша документация автоматически начнет появляться на главной странице.
Suhas

1

Genghis Command Line Parser может быть немного устаревшим, но он очень функциональный и хорошо работает для меня.


Печально, но у парсера Genghis Command Line нет документации.
Окиган

Если вы посмотрите на источники, есть пример, который показывает варианты использования. genghis.codeplex.com/SourceControl/changeset/view/9491#73699
devdimi

0

Я бы предложил библиотеку с открытым исходным кодом CSharpOptParse . Он анализирует командную строку и гидратирует определенный пользователем объект .NET с помощью ввода из командной строки. Я всегда обращаюсь к этой библиотеке при написании консольного приложения на C #.



0

Очень простой и удобный в использовании специальный класс для разбора командной строки, который поддерживает аргументы по умолчанию.

class CommandLineArgs
{
    public static CommandLineArgs I
    {
        get
        {
            return m_instance;
        }
    }

    public  string argAsString( string argName )
    {
        if (m_args.ContainsKey(argName)) {
            return m_args[argName];
        }
        else return "";
    }

    public long argAsLong(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToInt64(m_args[argName]);
        }
        else return 0;
    }

    public double argAsDouble(string argName)
    {
        if (m_args.ContainsKey(argName))
        {
            return Convert.ToDouble(m_args[argName]);
        }
        else return 0;
    }

    public void parseArgs(string[] args, string defaultArgs )
    {
        m_args = new Dictionary<string, string>();
        parseDefaults(defaultArgs );

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private void parseDefaults(string defaultArgs )
    {
        if ( defaultArgs == "" ) return;
        string[] args = defaultArgs.Split(';');

        foreach (string arg in args)
        {
            string[] words = arg.Split('=');
            m_args[words[0]] = words[1];
        }
    }

    private Dictionary<string, string> m_args = null;
    static readonly CommandLineArgs m_instance = new CommandLineArgs();
}

class Program
{
    static void Main(string[] args)
    {
        CommandLineArgs.I.parseArgs(args, "myStringArg=defaultVal;someLong=12");
        Console.WriteLine("Arg myStringArg  : '{0}' ", CommandLineArgs.I.argAsString("myStringArg"));
        Console.WriteLine("Arg someLong     : '{0}' ", CommandLineArgs.I.argAsLong("someLong"));
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.