Как обеспечить требуемые параметры командной строки с помощью NDesk.Options?
Я просто писал консольную утилиту и решил использовать NDesk.Options для синтаксического анализа командной строки. Мой вопрос: как мне выполнить требуемые параметры командной строки?
Я вижу в docs, что:
с требуемым значением (добавьте '=' к имени параметра) или необязательное значение (добавьте ':' к имени опции).
Однако, когда я помещаю a =
в конец имени параметра, нет разницы в поведении. В идеале метод Parse генерирует исключение.
Есть ли что-то еще, что мне нужно сделать?
Вот мой тестовый код:
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
Проблема заключается в том, что документация не так понятна, как кажется.: - (
В частности, согласно:
http://www.ndesk.org/doc/ndesk-options/NDesk.Options/OptionValueType.html#F:NDesk.Options.OptionValueType.Required
=
в спецификации параметра не применяется к OptionSet в целом, а только к значению для этой конкретной опции.
Важность этого действительно актуальна только в двух сценариях, поэтому сначала рассмотрим парсер OptionSet:
string a = null;
string b = null;
var options = new OptionSet {
{ "a=", v => a = v },
{ "b=", v => b = v },
};
Сценарий 1, где важно, что OptionSet.Parse() работает в однопроходном режиме только для прямого доступа и не смотрит на значения опций, чтобы определить, должны ли они "быть". Таким образом, рассмотрим:
options.Parse(new[]{"-a", "-b"});
Результатом этого будет то, что a
имеет значение "-b"
, а b
- null
. Поскольку обработчик для -a
требует значения, он всегда получает следующее значение (если только значение не "закодировано" в исходную опцию, например -a=value
).
Второе место, где это важно, - это параметр, требующий значения, последний параметр, и для него нет значения:
options.Parse(new[]{"-a"});
Это вызовет OptionException, так как обработчик для -a
требует значения, и никакого значения не будет.
Следовательно, если у вас есть опция, которая сама необходима (в отличие от опции, требующей значения), вам необходимо вручную проверить это:
string dir = null;
new OptionSet {
{ "o=", v => dir = v },
}.Parse (args);
if (dir == null)
throw new InvalidOperationException ("Missing required option -o=DIR");
Ответ 2
Можно немного расширить NDesk.Options, чтобы добавить эту функциональность.
Сначала создайте класс SetupOption, который будет реализовывать INotifyPropertyChanged:
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"));
}
}
}
}
Во-вторых, добавьте перегрузку в ActionOption, которая принимает экземпляр INotifyPropertyChanged в качестве аргумента (назовите его targetValue).
В-третьих, измените класс Option, чтобы добавить private INotifyPropertyChanged targetValue и private bool optionSet.
В-четвертых, передайте targetValue в Option при его создании. Подпишитесь на событие PropertyChanged. В нем установите для параметра "optionSet" значение true, если отправитель не является нулевым.
Добавьте метод Validate() в класс Option, который будет генерировать исключение, если targetValue не является нулевым, а optionSet - false.
Наконец, добавьте метод Validate() в OptionContext, который будет перебирать все параметры и вызывать их соответствующие методы Validate(). Вызовите его в самом конце метода Parse().
Вот zip измененного кода: http://www.davidair.com/misc/options.zip