Каковы хорошие привычки для разработки аргументов командной строки?

190

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

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

Мои вопросы:

  1. Является ли популярная -argument valueформула лучше чем argument:value(более читабельна, проще в написании, без ошибок, легче для понимания опытными разработчиками)?
  2. Существуют ли некоторые общеизвестные правила, которым я должен следовать при разработке аргументов командной строки (кроме того, если это работает, то все в порядке)?

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

Мы работаем над приложением, которое будет использоваться в общественных местах (сенсорные тотемы, столы). Приложения написаны с использованием Qt Quick 5 (C ++, QML, JS). На устройствах будет установлена ​​Windows 8.1 / 10. Мы предоставим интерфейсный интерфейс для управления устройствами. Однако некоторые продвинутые администраторы могут захотеть настроить приложение самостоятельно. Это не очень важно со стороны бизнеса, но, поскольку я согласен с тем, что сказал Килиан Фот, я не хочу, чтобы мое приложение доставляло пользователю боль. Не найдя в интернете то, что я хочу, я спросил здесь.


Для более опытных пользователей Stack Exchange: я хотел, чтобы этот вопрос был общим. Может быть, это относится к сообществу вики (я не знаю, можно ли преобразовать существующий вопрос с ответами). Поскольку я хочу, чтобы этот вопрос не зависел от операционной системы и языка программирования, ответы на него могут стать полезным уроком для других разработчиков.

Филипп Хазубский
источник
14
Посмотрите на популярные инструменты командной строки. Например, один дефис часто используется для объединения параметров. Например, вы можете написать, ls -ltrчтобы объединить варианты -l, -tи -r. Программы в стиле GNU также обычно допускают варианты на основе слов с двойным дефисом, как --reverseвместо -r. Существуют и другие популярные соглашения, такие как -hпоказ справки, --сигнализация об окончании опций, указание -в качестве имени файла разрешения на чтение из стандартного ввода и т. Д.
Brandin
72
Ни один -argument valueне -argument:valueраспространен. Общими являются -a value, -avalueи --argument=value.
reinierpost
39
Используйте популярную библиотеку синтаксического анализа командной строки (обычно называемую чем-то вроде getopt(s)), где вы можете.
reinierpost
5
@ k3b Мы работаем с Qt. Как сказал Кевин Клайн в своем комментарии, мы можем использовать уже имеющуюся библиотеку. Я полагаю, что он мультиплатформенный и продуманный. QCommandLineParser
Филип Хазубски
11
Проблема с вашим последним сообщением заключается в том, что разбор аргументов не является независимой от платформы проблемой.
pydsigner

Ответы:

237

В системах POSIX (например, Linux, MacOSX), по крайней мере, для программ, которые могут быть запущены в терминале оболочки (например, в большинстве из них), я бы рекомендовал использовать соглашения о кодировании GNU (в которых также перечислены общие имена аргументов) и заглянуть в рекомендации утилит POSIX. даже для проприетарного программного обеспечения:

  • всегда обрабатывать --versionи--help (даже /bin/trueпринимает их !!). Я проклинаю авторов программного обеспечения, не понимая --help, я ненавижу их (потому что prog --help это первая команда, которую я пробую в новой программе)! Часто --helpможет быть сокращено как-h

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

  • принять -aкороткий аргумент (одна буква) и иметь некоторый эквивалент --long-argument, поэтому -a2 --long-argument=2, --long-argument 2; конечно, вы можете иметь (для редко используемых опций) какое-то --only-long-argumentимя; поскольку модальные аргументы без дополнительных опций -cfобычно обрабатываются как -c -fи т. д., так что ваше -argument:valueпредложение странное, и я не рекомендую этого делать.

  • используйте GLIBC getopt_long или лучше (например, argp_parse , в OCaml это Argмодуль , ...)

  • часто используется -для стандартного ввода или вывода (если вы не можете этого сделать, работайте /dev/stdinи /dev/stdoutдаже в тех операционных системах, где их нет)

  • подражать поведению подобных программ путем повторного использования большинства их соглашений об опциях; в частности -nдля пробежек (а-ля make), -hдля помощи, -vдля многословия и т. д ...

  • использовать в --качестве разделителя между параметрами и файлом или другими аргументами

  • если ваша программа использует isattyдля проверки, чем stdin является терминалом (и в этом случае ведет себя «интерактивно»), предоставьте опцию принудительного включения неинтерактивного режима, аналогично, если ваша программа имеет интерфейс с графическим интерфейсом (и тестирует getenv("DISPLAY")на рабочем столе X11), но может также использоваться в пакетной или командной строке.

  • Некоторые программы (например gcc) принимают списки косвенных аргументов, поэтому @somefile.txtимеет смысл читать аргументы программы из somefile.txt; это может быть полезно, когда ваша программа может принимать очень много аргументов (больше, чем ваше ядро ARG_MAX)

Кстати, вы могли бы даже добавить некоторые функции автозаполнения для вашей программы и обычных оболочек (например, bashили zsh)

Некоторые старые команды Unix (например dd, или даже sed) имеют странные командные аргументы для исторической совместимости. Я бы порекомендовал не следовать их вредным привычкам (если только вы не делаете из них лучший вариант).

Если ваше программное обеспечение представляет собой ряд взаимосвязанных программ командной строки, черпает вдохновение из мерзавца (который вы , конечно , использовать в качестве инструмента развития), который принимает git helpи git --helpи есть много gitsubcommandиgitsubcommand--help

В редких случаях вы также можете использовать argv[0](используя символические ссылки в вашей программе), например, bashвызывается как rbashимеет другое поведение ( ограниченная оболочка). Но я обычно не рекомендую делать это; Это может иметь смысл, если ваша программа может использоваться как интерпретатор сценариев с использованием shebang, т.е. #!в первой строке, интерпретируемой execve (2) . Если вы делаете такие трюки, обязательно документируйте их, в том числе в --helpсообщениях.

Помните , что на POSIX оболочка является подстановкой аргументов ( перед тем запуском программы!), Поэтому не требуют символов (например , *или $или ~) в вариантах , которые должны были бы быть скорлупой маскирования.

В некоторых случаях вы можете встроить интерпретатор, такой как GNU guile или Lua, в свое программное обеспечение (не изобретайте свой собственный язык сценариев, полный Turing, если вы не разбираетесь в языках программирования). Это имеет глубокие последствия для дизайна вашего программного обеспечения (так что об этом следует думать заранее!). После этого вы легко сможете передать некоторый сценарий или выражение этому интерпретатору. Если вы выберете такой интересный подход, тщательно продумайте свое программное обеспечение и его интерпретируемые примитивы; у вас может быть какой-то странный пользователь, кодирующий большие сценарии для вашей вещи.

В других случаях вы можете позволить своим опытным пользователям загружать свои плагины в ваше программное обеспечение (используя методы динамической загрузки à la dlopen& dlsym). Опять же, это очень важное проектное решение (поэтому определите и задокументируйте интерфейс плагина с осторожностью), и вам нужно будет определить соглашение для передачи параметров программы этим плагинам.

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


Обратите внимание, что /optionэто Windows или VMS. Это было бы безумием в системах POSIX (потому что файловая иерархия использует /в качестве разделителя каталогов, а оболочка выполняет глобализацию). Весь мой ответ в основном для Linux (и POSIX).


PS Если возможно, сделайте вашу программу свободным программным обеспечением , вы получите улучшения от некоторых пользователей и разработчиков (и добавление новой опции программы часто является одной из самых простых вещей, которую можно добавить к существующему бесплатному программному обеспечению). Кроме того , ваш вопрос зависит много от предполагаемой аудитории : игра для подростков или браузер для бабушки , вероятно , не нужен такой же вид и количество опций , чем компилятор, или сетевой инспектор для центров обработки данных , сисадминов, или программное обеспечение САПР для микропроцессора архитекторы или для мостовых дизайнеров. Инженеру, знакомому с программированием и сценариями, вероятно, больше нравится иметь много настраиваемых параметров, чем у вашей бабушки, и, возможно, он захочет запустить ваше приложение без X11 (возможно, на crontabработе).

Василий Старынкевич
источник
18
Договорились, но git это лучший пример. Я не буду рекомендовать даже глядя в cvsв 2016 году
Basile Starynkevitch
8
+ в случае неправильной опции просто показать справку. SOOOO раздражает, например, получить бесполезное сообщение об ошибке dd -h.
Домен 15.01.16
8
Программы GNU, --helpкак правило, поддерживают, но часто не распознают, -hчто раздражает. Я обычно набираю -h, когда забываю опцию в программе, раздражает необходимость повторного ввода команды с более длинной опцией --help. В конце концов, я напечатал -h, потому что я что-то забыл. Почему пользователь должен помнить, какие программы требуют «--help», а какие программы требуют «-h» для отображения экрана справки? Просто включите опцию для -h и --help.
Брандин
30
Первая часть этого ответа хороша, но где-то рядом вы как бы сходите с ума по касательным. Например, файлы конфигурации, плагины и dlopen, выбор лицензий на программное обеспечение и веб-браузеры больше не связаны с соглашениями об интерфейсе командной строки.
Брандин
5
И пожалуйста. Если вы отформатируете вывод для экрана, добавьте переопределение формата вывода. Ничто не раздражает меня больше, чем команды, которые обрезают или переносят строки, когда я пытаюсь использовать их в сценарии.
Sobrique
68

Тот факт, что соглашение о формате данных является популярным, является его преимуществом.

Вы можете легко увидеть, что использование = или: или даже '' в качестве разделителя - это тривиальные различия, которые могут быть преобразованы в друг друга с помощью компьютера без особых усилий. Что было бы большим усилием для человека, чтобы вспомнить: «Теперь, видите, эта редко используемая программа разграничивает вещи с :или с =? Хммм ...»

Другими словами, ради любви к Богу, не отклоняйтесь от чрезвычайно укоренившихся соглашений без веской причины. Люди будут помнить вашу программу как «ту, у которой странный и раздражающий синтаксис cmdline» вместо «той, которая спасла мое эссе из колледжа».

Килиан Фот
источник
19
почти для всех языков есть библиотечные функции для обработки аргументов командной строки. Используйте один из них.
Кевин Клайн
9
Хороший совет, но каковы эти соглашения? -1
RubberDuck
14
Существует интересный пример широко используемого инструмента UNIX, который намеренно нарушает это соглашение: ddиспользует key=valueсинтаксис. Причиной такого конструктивного решения является то, что этот инструмент (псевдоним: d ata d estroyer) может нанести большой ущерб при неправильном использовании. Заставляя пользователя отказаться от привычной привычки, он заставляет его задуматься о том, что он делает.
Филипп
18
Вы уверены, что это причина dd? Я считаю, что он просто был закодирован в то время (1970-е годы?), Когда ядро ​​ARG_MAX было маленьким, оболочки не имели автозаполнения и --long-argumentsне существовали. С тех пор лучше ddоставаться обратно совместимым
Василий Старынкевич
9
ddпроизошел от другой ОС (кроме UNIX) - языка сценариев (JCL) в IBM OS / 360 - где соглашения были иными, прежде чем были перенесены более или менее неизменными в UNIX - отчасти потому, что те, кто мог его использовать, знали об этом из предыдущая система.
Баард Копперуд
29

С точки зрения непрофессионала

Когда в Риме, делай, как римляне.

  • Если ваше приложение CLI предназначено для Linux / Unix, используйте соглашение -p valueили --parameter value. В Linux есть инструменты для простого анализа таких параметров и флагов.

Я обычно делаю что-то вроде этого:

while [[ $# > 0 ]]
do
key="$1"
case $key in
    --dummy)
    #this is a flag do something here
    ;;
    --audit_sessiones)
    #this is a flag do something here
    ;;
    --destination_path)
    # this is a key-value parameter
    # the value is always in $2 , 
    # you must shift to skip over for the next iteration
    path=$2
    shift
    ;;
    *)
    # unknown option
    ;;
esac
shift
done
  • Если ваше приложение CLI предназначено для Windows, тогда используйте /flagи /flag:valueусловные обозначения.

  • Некоторые приложения, такие как Oracle, не используют ни того, ни другого. Утилиты Oracle используют PARAMETER=VALUE.

  • Одна вещь, которую мне нравится делать, - это, помимо принятия параметров в командной строке, предоставить возможность использования parfile , который является файлом пары ключ-значение, чтобы избежать длинных цепочек параметров. Для этого вы должны предоставить дополнительный --parfile mifile.parпараметр. Очевидно, что если --parfileиспользуется, все остальные параметры отбрасываются в пользу того, что находится внутри файла.

  • Дополнительным предложением является возможность использования некоторых пользовательских переменных среды , например, установка переменной среды MYAPP_WRKSPACE=/tmpсделает ненужным постоянную установку --wrkspace /tmp.

  • В Linux не забудьте добавить параметр автозаполнения , что означает, что пользователи могут ввести половину переключателя, нажать, TABа затем оболочка завершит его для них.
Тулаинс Кордова
источник
1
Ну, несколько утилит GNU (например gcc) обрабатывают @mifile.parкак ваши --parfile mifile.parпредложения.
Василий Старынкевич
7
Вы уверены, что игнорируете все остальные опции, если есть файл параметров? Это звучит нелогично для меня, в лучшем случае.
Джонатан Леффлер
8
Я согласен с Джонатаном по поводу файлов параметров. Если файл параметров присутствует, то я ожидаю, что он будет использоваться для значений по умолчанию, а аргументы, заданные в командной строке, будут применены поверх аргументов в файле parfile. Если по какой-либо причине использование файла parfile исключает использование дополнительных аргументов командной строки, то наличие дополнительных аргументов должно быть ошибкой.
Eldritch Cheese
@EldritchCheese В приложениях CLI, которые я написал, когда задан файл, любой дополнительный параметр генерирует ошибку.
Тулаинс Кордова
1
Если вы используете способную оболочку, такая опция «параметр файла конфигурации» не обязательна. например, ты просто пишешь fooCmd -opt1 -opt2 $(cat more_options.opts). Поэтому я ожидаю, что опция «параметр файла конфигурации», если она предусмотрена, должна работать в основном таким же образом.
Брандин
19

Одна вещь, которая еще не подошла:

Попробуйте разработать свое программное обеспечение от аргументов командной строки вверх. Смысл:

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

Это позволит вам детально изучить крайние случаи и общие случаи на ранней стадии. Конечно, вы все равно будете абстрагировать внешнее и внутреннее, но это даст гораздо лучший результат, чем просто написание всего кода и затем добавление к нему CLI.

Кроме того, проверьте docopt ( http://docopt.org/ ).

docopt очень помогает во многих языках, особенно для python, где у вас есть строго ограниченные парсеры неблагоприятных для пользователя аргументов, такие как argparse, которые по-прежнему рассматриваются как «ОК». Вместо парсеров, подпарасеров и условных диктов, вы просто определяете синтаксическую справку, а она делает все остальное.

Флориан Хейгл
источник
Мне нравится этот ответ, и спасибо за него, потому что в настоящее время я разочарован argparseтем , что не являюсь ни программистом, ни пользовательским интерфейсом, ни дружественным к пользователю.
кошка
Я попробовал докопт, и мне это не нравится. щелчок приводит к намного более чистому коду, хотя есть некоторые неудобства с опциями, когда у вас есть подкоманды.
jpmc26
2
Это слишком похоже на рекламу. Упомяните ресурс только один раз, если он уместен. Но как и сейчас, 50% вашего ответа звучит как продвижение внешнего ресурса.
Брандин
Я бы назвал это «сначала разработайте модель использования». Во многих случаях отделение пользовательского опыта от функциональности может быть искусственным отличием (ограничения интерфейса влияют на интерфейс).
медь.
1
+1 за Докопта. Это решило все мои дилеммы CLI, совершенно безболезненно. Иногда трудно отличить рекламу от подлинного энтузиазма, но вот она - я уже много лет энтузиаст Docopt, никак не связана с ней;)
frnhr
3

Некоторые ценные комментарии уже предоставлены (@Florian, Basile), но позвольте мне добавить ... OP говорит,

Мы предоставим интерфейсный интерфейс для управления устройствами. Однако некоторые продвинутые администраторы могут захотеть настроить приложение самостоятельно

Но также замечания:

Я не хотел, чтобы этот вопрос зависел от платформы или языка

Вы должны учитывать свою целевую аудиторию - продвинутых администраторов . На какой платформе они обычно работают - Win / Unix / Mac? А на какой платформе вы запускаете приложение? Следуйте тем соглашениям CLI, которые уже созданы для этой платформы. Ваши "продвинутые" администраторы хотят / нуждаются в инструменте на основе GUI?

Вы хотите, чтобы интерфейс был согласованным внутри и с другими инструментами администратора. Я не хочу останавливаться и думать, это cmd -p <arg>или cmd -p:<arg>или cmd /p <arg>. Нужны ли цитаты, потому что есть пробел? Могу ли я cmd -p <val1> <val2>или cmd -p <val1> -p <val2>для нескольких целей? Они заказывают конкретно? Перегружаемые? Тоже cmd -p2 <arg> -p1 <arg>работает? Есть ли ls -l -r -t dir1 dir2== ls -trl dir1 dir2?

Что касается инструментов администрирования Unix, я всегда помнил руководство Shelldorado от Heiner's вместе с другими упомянутыми ссылками.

Не менее важно, чем проектирование интерфейса командной строки, убедиться, что ваше приложение разработано для работы с аргументами командной строки, такими же, как в графическом интерфейсе, то есть: нет бизнес-логики в графическом интерфейсе или использовать общую команду, вызываемую как из графического интерфейса, так и из интерфейса командной строки.

Большинство основанных на UNIX инструментов администрирования фактически спроектированы вначале как инструменты командной строки, а предоставленный графический интерфейс пользователя просто облегчает «заполнение» параметров командной строки. Такой подход позволяет автоматизировать, использовать файлы ответов и т. Д., А также осуществлять управление на расстоянии (меньше работы для меня!)

В качестве классического набора инструментов используется этот подход - Tcl / Tk . Не предлагает вам переключать инструменты; просто рассмотрите подход к проектированию, начиная с написания административного приложения на основе GUI и заканчивая приложением в качестве инструмента командной строки; затем слой GUI сверху для удобства. В какой-то момент вы, вероятно, обнаружите, что графический интерфейс является болезненным (и подверженным ошибкам), если вам придется выполнять несколько конфигураций и повторно вводить одни и те же параметры снова и снова, и вы будете искать автоматический подход.

Помните, что ваш администратор, вероятно, все равно должен вводить правильные значения в правильные поля, так сколько усилий вы все равно освобождаете с помощью GUI?

Ян В.
источник
Отлично, да! Один вопрос также: могу ли я запустить ./this-script --hosts <hosts.txt
Флориан Хейгл