Які хороші звички для проектування аргументів командного рядка?


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показати допомогу, --подати сигнал про закінчення параметрів, вказавши -як ім'я файлу, щоб дозволити читати з stdin тощо.
Брандін

72
Ні вони -argument valueне -argument:valueє загальними. Спільними є -a value, -avalueі --argument=value.
reinierpost

39
Використовуйте популярну бібліотеку для розбору командного рядка (зазвичай її називають щось на зразок getopt(s)), де можна.
reinierpost

5
@ k3b Ми працюємо з Qt. Як сказав у своєму коментарі Кевін Клайн, ми можемо використовувати вже наявну бібліотеку. Я гадаю, це багатоплатформна і продумана. QCommandLineParser
Філіп Хазубський

11
Проблема остаточного розмиття полягає в тому, що аналіз аргументів не є проблемою, незалежною від платформи.
підсигнант

Відповіді:


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 є терміналом (і в такому випадку поводиться "інтерактивно"), надайте можливість застосувати неінтерактивний режим, також якщо ваша програма має інтерфейс GUI (і тести 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 (уникайте вигадування вашої власної мови сценаріїв, повного Тьюрінга, якщо ви не знаєте мов програмування). Це має глибокі наслідки для дизайну вашого програмного забезпечення (так слід думати про ранні терміни!). Тоді ви повинні легко мати можливість передати певний сценарій або якийсь вираз цьому інтерпретатору. Якщо ви ставитесь до цього цікавого підходу, обережно розробіть програмне забезпечення та його інтерпретовані примітиви; у вас можуть бути якісь дивні користувацькі кодування великих сценаріїв для вашої речі.

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

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


Зауважте, що /optionце річ Windows або VMS. Це було б божевільно в системах POSIX (тому що ієрархія файлів використовує /як сепаратор каталогів і тому, що оболонка робить глобальну). Вся моя відповідь здебільшого стосується Linux (та POSIX).


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


18
Домовились, але git це кращий приклад. Я не буду рекомендувати навіть дивлячись в cvsв 2016 році
Basile Starynkevitch

8
+ у разі недійсного варіанту просто покажіть довідку. Наприклад, SOOOO дратує отримати непотрібне повідомлення про помилку dd -h.
Domen

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) в ОС OS / 360 IBM - де конвенції були різними, до того як вони були перенесені більш-менш незмінними в 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 mifile.parпараметр. Очевидно, що якщо --parfileвони використовуються, всі інші параметри відкидаються на користь того, що знаходиться всередині парфіла.

  • Додатковою пропозицією є використання деяких змінних користувальницького середовища , наприклад, встановлення змінної середовища MYAPP_WRKSPACE=/tmpзробить непотрібним завжди встановлювати --wrkspace /tmp.

  • У Linux не забудьте додати параметр автоматичного завершення , тобто користувачі можуть набрати половину комутатора, натиснути, TABа потім оболонка завершить їх для них.

1
Ну, кілька утиліт GNU (наприклад gcc) обробляють, @mifile.parяк ваші --parfile mifile.parпропозиції.
Василь Старинкевич

7
Ви впевнені, що ігноруєте всі інші параметри, якщо є файл параметра? Мені це в кращому випадку звучить протиінтуїтивно.
Джонатан Леффлер

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

@EldritchCheese У програмах CLI, які я писав, коли надається парфіл, будь-який додатковий параметр генерує помилку.
Тулен Кордова

1
Якщо використовується дієздатна оболонка, такий варіант "параметру конфігураційного файлу" не потрібен. наприклад, ви просто пишете fooCmd -opt1 -opt2 $(cat more_options.opts). Тому я б очікував, що параметр "config file parameter", якщо він передбачений, в основному повинен працювати так само.
Брандін

19

Одне, що ще не придумали:

Спробуйте створити програмне забезпечення від аргументів командного рядка вгору. Значення:

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

Це дозволить вам розгортати крайові випадки та звичайні випадки на початку. Звичайно, ви все одно абстрагуєте зовнішню і внутрішню сторону, але це дасть набагато кращий результат, ніж просто написати весь код і потім застукати CLI до нього.

Крім того, ознайомтеся з docopt ( http://docopt.org/ ).

docopt - це велика допомога в цьому для багатьох мов, особливо для python, де у вас сильно обмежені аналізатори аргументів, сприятливих для користувачів, як argparse, як і раніше, вважаються "ОК". Замість того, щоб мати парсери та підрозділи та умовні дикти, ви просто визначте довідку синтаксису, і вона зробить все інше.


Я люблю цю відповідь і дякую за це, тому що в даний час я засмучений argparseтим, що не є програмістом, користувальницьким інтерфейсом чи зручним для користувачів.
кіт

Я спробував docopt, і мені це не подобається. клацання призводить до набагато чистішого коду, хоча у варіантів, коли у вас є підкоманди.
jpmc26

2
Це занадто схоже на рекламу. Згадуйте про ресурс лише один раз, якщо це доречно. Але, як це є зараз, 50% вашої відповіді просто звучить як просування зовнішнього ресурсу.
Брандін

Я б назвав це як "спочатку спроектуйте модель використання". Відокремлення користувальницького досвіду від функціоналу може бути штучною відмінністю у багатьох випадках (обмеження інструментів впливають на інтерфейс).
мідь.ха

1
+1 для Docopt. Це вирішило всі мої дилеми CLI, повністю безболісно. Іноді важко сказати рекламу від щирого ентузіазму, але ось це - я вже довгі роки є ентузіастом Docopt, жодним чином не пов’язаний з нею;)
frnhr

3

Деякі цінні коментарі вже надані (@Florian, Basile), але дозвольте додати ... ОП говорить,

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

Але також зауваження:

Я не хотів, щоб це питання було певним або мовним

Ви повинні врахувати свою цільову аудиторію - розширені адміністратори . На якій платформі вони зазвичай працюють - 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, я завжди пам’ятав керівництво, надане Шельдорадо Хайнера, а також інші згадки.

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

Більшість адміністративних інструментів на основі UNIX насправді розроблені спочатку як інструменти командних рядків, а наданий графічний інтерфейс просто полегшує "заповнення" параметрів командного рядка. Такий підхід дозволяє автоматизувати, використовувати файли відповідей тощо, та керувати руками (менше роботи для мене!)

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

Пам'ятайте, що ваш адміністратор, швидше за все, повинен вводити правильні значення у правильні поля, тож скільки зусиль ви все-таки позбавляєте w / GUI?


Чудово, так! Одне питання також: чи можу я запустити ./this-script --hosts <hosts.txt
Флоріан Хейгл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.