Найкращий спосіб проаналізувати аргументи командного рядка в C #? [зачинено]


731

Створюючи консольні програми, які приймають параметри, ви можете використовувати аргументи, передані Main(string[] args).

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

Тож мене цікавить:

  • Бібліотеки, якими ви користуєтесь
  • Шаблони, які ви використовуєте

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


Попередня дискусія, роздільна-рядок, що містить-параметри командного рядка-в-рядок-в-с # , може мати деякі відповіді.
gimel

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

44
@ call me Steve: суть аргументів командного рядка полягає в тому, що вони можуть змінюватись за кожний виклик - як це зробити з налаштуваннями програми?
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
Frank Schwieterman

5
Коли у мене є одне додаток з декількома різними командами, я "шарую" OptionSets. Візьміть 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
NDesk для мене не працює. Не вдалося прочитати цілі аргументи нормально, але не рядки. Змінні продовжують отримувати аргументи (наприклад, '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 у випадку підтримка зникає.
Чарльз Бернс

Я також рекомендую це. Моя єдина проблема полягала в тому, що: визначення дозволеної комбінації аргументів (наприклад, якщо аргументація з переміщенням повинна мати аргументи джерела та dest) також може бути пов'язана з атрибутами. Але вам, можливо, краще робити це за окремою логікою підтвердження аргументів
Ліндон Уайт

1
Мені подобається клас «Параметри». Схоже, вони підтримують неназвані параметри та прапори, як --recursiveтеж.
Wernight

2
Я лише перевірив це, і я реалізував варіант для свого застосування всього за кілька хвилин. Користуватися бібліотекою надзвичайно просто.
Trismegistos

3
Я вважав цю бібліотеку дуже обмежувальною для себе. Якщо вам потрібні ексклюзивні набори, ви не можете визначити необхідні параметри для кожного набору, тому доведеться перевіряти їх вручну. Ви не можете визначити мінімальну вимогу для неназваних значень, їх також потрібно перевірити вручну. Довідковий конструктор екранів також зовсім не гнучкий. Якщо поведінка бібліотеки не відповідає вашим потребам, ви можете практично нічого не змінити.
Сергій Коструков

50

Бібліотека WPF 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), а не через інструмент сторонніх виробників, навіть якщо підтримка постачальника надходить наскрізно.
Joel Coehoorn

2
Однак, прийнята відповідь (моно) - наступне найкраще.
Joel Coehoorn

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
@Adam Oren: моя відповідь - 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 для істинного та / b- для хибного) або додавши значення true / false, так / ні тощо.
  • Масиви аргументів: просто додайте декілька значень після імені командного рядка, щоб встановити властивість, що визначається як масив. Наприклад, / x 1 2 3 заповнить x масивом {1, 2, 3} (якщо при цьому x визначено як масив цілих чисел).
  • Псевдоніми командного рядка: Властивість може підтримувати для нього кілька псевдонімів командного рядка. Наприклад, Help використовує псевдонім ?.
  • Часткове розпізнавання імен: Вам не потрібно вказувати повне ім’я або псевдонім, достатньо написання, щоб аналізатор розмежував властивість / псевдонім від інших.
  • Підтримує ClickOnce: може ініціалізувати властивості навіть тоді, коли вони вказані як рядок запиту в URL-адресі для розгорнутих додатків ClickOnce. Метод ініціалізації командного рядка визначить, працює він як ClickOnce чи ні, тому код не потребує змін при його використанні.
  • Автоматично створює /? довідка. Сюди входить приємне форматування, яке враховує ширину консолі.
  • Завантаження / збереження аргументів командного рядка у файл: Це особливо корисно, якщо у вас є кілька великих, складних наборів аргументів командного рядка, які потрібно запустити кілька разів.

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


9

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


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

@Wernight ви можете використовувати параметр IsDefault у дієслові do, щоб його можна було опустити. Я не знайшов підтримки для позиційних параметрів, але я використовував позиційні параметри лише тоді, коли сам розбирав командний рядок. Набагато чіткіше використовувати аргументи з назвою, за якими слідують значення IMHO.
Луденв'є

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, Список рядків, 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);
        }
    }
}

Редагувати: це мій проект, і як таку відповідь не слід розглядати як схвалення третьої сторони. Це сказав, що я використовую його для кожної програми, заснованої на командному рядку, яку я пишу, вона є відкритим кодом, і я сподіваюся, що інші можуть отримати від неї користь.


Просто FYI, що ви повинні поставити невелику відмову від того, що ви пов’язані з проектом Rug.Cmd (як згадувалося в FAQ): stackoverflow.com/faq#promotion - Не велика справа, оскільки ви рекламуєте відкриті- вихідний проект, але все-таки добре додати відмову;) +1 до речі ... виглядає досить добре.
Jason Down

Будьте привітані за те, що я вдячний і подякував +1, я переконуюсь, що я більш чітко висловлююся про свою приналежність.
Phill Tew

Не хвилюйтесь ... Є такі наклейки для такого типу речей (я не один з них), тому мені подобається людям підняти голову. Знову ж таки, це не є проблемою для проектів з відкритим кодом. Це здебільшого не дозволяє людям спамувати рекомендації щодо своїх (платних) продуктів.
Jason Down

3

На веб- сайті http://www.codeplex.com/commonlibrarynet є аналізатор аргументів командного рядка

Він може аналізувати аргументи, використовуючи
1. атрибути
2. явні виклики
3. один рядок з декількох аргументів АБО рядковий масив

Він може обробляти такі речі, як:

- config : Qa - startdate : $ { today } - region : 'New York' Settings01

Це дуже просто у використанні.


2

Це обробник, який я написав на основі Optionsкласу Novell .

Цей орієнтований на консольні програми, які виконують 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 також пропонує проект, що містить приємний набір тестів Unit, які говорять більше про функціональність, ніж будь-яка документація могла
  • він має приємний об'єктно-орієнтований дизайн, відсутність повторення коду чи інших подібних речей, які я мав у своїх програмах для розбору командного рядка
  • це декларативно: ви в основному пишете класи для команд і наборів параметрів і прикрашаєте їх атрибутами для встановлення різних параметрів (наприклад, ім'я, опис, обов'язковий / необов'язковий)
  • бібліотека навіть друкує хороший графік використання, виходячи з цих визначень

Нижче наводиться простий приклад того, як це використовувати. Для ілюстрації використання я написав просту утиліту, яка має дві команди: - додати (додає об’єкт до списку - об’єкт складається з імені (рядок), значення (int) та булевого прапора) - список (списки всі додані об'єкти)

Перш за все, я написав клас 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
  ------------------------

І вибірки використання команди "додати":

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 Commandlets.

Розбір виконаний на основі атрибутів, зазначених у командлетах, підтримка перевірок, наборів параметрів, конвеєрного опису, повідомлення про помилки, довідка та найкраще повернення об’єктів .NET для використання в інших командлетах.

Кілька посилань я знайшов корисним для початку:


2

C # CLI - це дуже проста бібліотека розбору аргументів командного рядка, яку я написав. Це добре задокументоване та відкрите джерело.


Добре задокументовано? Де документація?
Suhas

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

Гаразд, я поспішно прокоментував. Можливо, ви можете перенести свій проект на github, і ваша документація автоматично почне відображатися на домашній сторінці.
Сухас

1

Парзатор Genghis Command Line Parser може бути трохи застарілим, але він дуже функціональний і працює для мене досить добре.


Сумно, але в програмі Genghis Command Line Parser немає жодної документації.
окіган

Якщо ви подивитесь на джерела, є зразок, який показує варіанти використання. 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.