Jakie są dobre nawyki przy projektowaniu argumentów wiersza poleceń?

190

Podczas tworzenia aplikacji zacząłem się zastanawiać - jak zaprojektować argumenty wiersza poleceń?

Wiele programów używa formuł takich jak ten -argument valuelub /argument value. Rozwiązanie, które przyszło mi do głowy, to argument:value. Pomyślałem, że to dobrze, ponieważ bez białych spacji nie ma mowy o pomyleniu wartości i argumentów. Łatwo jest również podzielić ciąg na dwa na pierwszym od lewej :postaci.

Moje pytania to:

  1. Czy popularna -argument valueformuła jest lepsza niż argument:value(bardziej czytelna, łatwiejsza do napisania, wolna od błędów, łatwiejsza do zrozumienia przez ekspertów programistów)?
  2. Czy są jakieś powszechnie znane reguły, których powinienem przestrzegać podczas projektowania argumentów wiersza poleceń (inne niż jeśli działa, to jest OK)?

Poprosiłem o więcej szczegółów. Podam je. Myślę jednak, że nie powinny wpływać na odpowiedzi. Pytanie dotyczy ogólnie dobrych nawyków. Myślę, że wszystkie są takie same dla wszystkich rodzajów aplikacji.

Pracujemy nad aplikacją, która będzie używana w miejscach publicznych (dotykowe totemy, tabele). Aplikacje są pisane przy użyciu Qt Quick 5 (C ++, QML, JS). Urządzenia będą miały zainstalowany system Windows 8.1 / 10. Zapewniamy interfejs front-end do zarządzania urządzeniami. Jednak niektórzy zaawansowani administratorzy mogą chcieć samodzielnie skonfigurować aplikację. Nie jest to bardzo ważne ze strony biznesu, ale ponieważ zgadzam się z tym, co powiedział Kilian Foth , nie chcę, aby moja aplikacja była uciążliwa dla użytkownika. Nie znajdując w Internecie tego, czego chcę, zapytałem tutaj.


Do bardziej zaawansowanych użytkowników stosu wymiany: chciałem, aby to pytanie było ogólne. Może kwalifikuje się do wiki społeczności (nie wiem, czy istniejące pytanie można przekonwertować za pomocą odpowiedzi). Ponieważ chcę, aby to pytanie było niezależne od systemu operacyjnego i języka programowania, pojawiające się tutaj odpowiedzi mogą być cenną lekcją dla innych programistów.

Filip Hazubski
źródło
14
Zobacz popularne narzędzia wiersza poleceń. Na przykład pojedynczy łącznik jest często używany, aby umożliwić łączenie opcji. np. możesz napisać, ls -ltraby połączyć opcje -l, -ti -r. Programy w stylu GNU zazwyczaj zezwalają również na opcje oparte na słowach z podwójnym myślnikiem --reversezamiast -r. Istnieją inne popularne konwencje, takie jak -hpokazywanie pomocy, --sygnalizowanie końca opcji, określanie -jako nazwy pliku umożliwiającej czytanie ze standardowego wejścia itp.
Brandin
72
Ani -argument valuenie -argument:valuesą powszechne. Powszechne są -a value, -avaluei --argument=value.
reinierpost
39
Skorzystaj z popularnej biblioteki do analizowania wiersza poleceń (zwykle nazywanej czymś podobnym getopt(s)), gdzie możesz.
reinierpost
5
@ k3b Pracujemy z Qt. Jak powiedział Kevin Cline w swoim komentarzu , możemy użyć już dostępnej biblioteki. Podejrzewam, że jest wieloplatformowy i dobrze przemyślany. QCommandLineParser
Filip Hazubski
11
Problem z ostatecznym wystąpieniem polega na tym, że analiza argumentów nie jest kwestią niezależną od platformy.
pydsigner

Odpowiedzi:

237

W systemach POSIX (np. Linux, MacOSX), przynajmniej w przypadku programów, które mogą być uruchamiane w terminalu powłoki (np. Większość z nich), zaleciłbym użycie konwencji kodowania GNU (które również zawierają listę popularnych nazw argumentów) i przyjrzenie się wytycznym narzędzi POSIX , nawet w przypadku oprogramowania zastrzeżonego:

  • zawsze obsługuj --versioni--help (nawet /bin/trueje akceptuje !!). Przeklinam autorów oprogramowania, którzy nie rozumieją --help, nienawidzę ich (bo prog --help to pierwsze polecenie, które próbuję na nowym programie)! Często --helpmożna je skrócić jako-h

  • Poproś, aby --helplista zawierała wszystkie opcje (chyba że masz ich zbyt wiele ... w takim przypadku wypisz najczęściej używane i wyraźnie odnieś się do jakiejś manstrony lub adresu URL) i wartości domyślne opcji, a być może ważne (i specyficzne dla programu ) Zmienne środowiska. Pokaż te listy opcji w przypadku błędu argumentu opcji.

  • zaakceptować -akrótki argumentu (jedna litera) i mieć jakiś odpowiednik --long-argument, tak -a2 --long-argument=2, --long-argument 2; oczywiście możesz mieć (dla rzadko używanych opcji) jakąś --only-long-argumentnazwę; dla argumentów modalnych bez dodatkowych opcji -cfjest zwykle traktowany jako -c -fitp., więc twoja -argument:valuepropozycja jest dziwna i nie polecam tego robić.

  • użyj GLIBC getopt_long lub lepszego (np. argp_parse , w OCaml jest to Argmoduł , ...)

  • często wykorzystują -do standardowego wejścia lub wyjścia (jeśli nie można tego zrobić, uchwyt /dev/stdini /dev/stdoutnawet na kilka systemów operacyjnych ich nie posiadające)

  • naśladować zachowanie podobnych programów poprzez ponowne wykorzystanie większości konwencji opcji; w szczególności -nna sucho (à la make), -hpo pomoc, -vza gadatliwość itp.

  • użyj --jako separatora między opcjami i plikiem lub innymi argumentami

  • jeśli twój program używa isattydo testowania niż stdin jest terminalem (w takim przypadku zachowuje się „interaktywnie”), zapewnij opcję wymuszenia trybu nieinteraktywnego, podobnie jeśli twój program ma interfejs GUI (i testy getenv("DISPLAY")na pulpicie X11), ale może również być używane w partii lub wierszu poleceń.

  • Niektóre programy (np. gcc) Akceptują pośrednie listy argumentów, co @somefile.txtoznacza odczyt argumentów programu z somefile.txt; może to być przydatne, gdy twój program może zaakceptować bardzo dużą liczbę argumentów (więcej niż jądro ARG_MAX)

BTW, możesz nawet dodać funkcje automatycznego uzupełniania dla swojego programu i zwykłe powłoki (jak bashlub zsh)

Niektóre stare polecenia uniksowe (np. ddLub nawet sed) mają dziwne argumenty poleceń dla historycznej zgodności. Radziłbym nie podążać za ich złymi nawykami (chyba że robisz jakiś lepszy wariant).

Jeśli oprogramowanie jest seria programów związanych z wiersza polecenia, czerpać inspirację z git (co na pewno użyć jako narzędzie rozwoju), która akceptuje git helpi git --helpi mają wiele gitsubcommandigitsubcommand--help

W rzadkich przypadkach możesz także użyć argv[0](używając dowiązań symbolicznych w swoim programie), np. bashWywołać, ponieważ rbashma inne zachowanie ( ograniczona powłoka). Ale zwykle nie polecam tego robić; może mieć sens, jeśli twój program może być używany jako interpreter skryptów za pomocą shebang, tj. #!w pierwszej linii interpretowanej przez execve (2) . Jeśli wykonujesz takie sztuczki, pamiętaj o ich udokumentowaniu, w tym w --helpwiadomościach.

Pamiętaj, że na POSIX powłoka jest globbing argumenty ( zanim uruchomiony program!), Więc uniknąć konieczności znaków (jak *lub $czy ~) w opcji, które musiałyby być powłoki uciekł.

W niektórych przypadkach możesz wbudować interpreter taki jak GNU Guile lub Lua w swoim oprogramowaniu (unikaj wymyślania własnego języka skryptowego pełnego Turinga, jeśli nie jesteś ekspertem w językach programowania). Ma to głębokie konsekwencje dla projektu twojego oprogramowania (należy o tym pomyśleć wcześnie!). Powinieneś wtedy łatwo przekazać skryptowi lub wyrażeniu temu tłumaczowi. Jeśli zastosujesz to interesujące podejście, ostrożnie zaprojektuj swoje oprogramowanie i jego interpretowane prymitywy; możesz mieć dziwnego użytkownika kodującego duże skrypty dla swojej rzeczy.

W innych przypadkach możesz pozwolić zaawansowanym użytkownikom na załadowanie wtyczki do twojego oprogramowania (przy użyciu technik dynamicznego ładowania à la dlopen& dlsym). Ponownie, jest to bardzo ważna decyzja projektowa (więc ostrożnie zdefiniuj i udokumentuj interfejs wtyczki) i musisz zdefiniować konwencję, aby przekazywać opcje programu do tych wtyczek.

Jeśli twoje oprogramowanie jest skomplikowane, pozwól mu akceptować niektóre pliki konfiguracyjne (oprócz lub zastępowanie argumentów programu) i prawdopodobnie masz jakiś sposób na przetestowanie (lub po prostu parsowanie) tych plików konfiguracyjnych bez uruchamiania całego kodu. Na przykład agent przesyłania poczty (taki jak Exim lub Postfix) jest dość złożony i przydatna jest możliwość „półsuchego” jego uruchomienia (np. Obserwowanie, jak obsługuje określony adres e-mail bez faktycznego wysyłania wiadomości e-mail).


Zauważ, że /optionjest to Windows lub VMS. Byłoby to szalone w systemach POSIX (ponieważ hierarchia plików używa /jako separatora katalogów i ponieważ powłoka wykonuje globowanie). Cała moja odpowiedź dotyczy głównie Linuksa (i POSIX).


PS Jeśli to możliwe, uczyń swój program wolnym oprogramowaniem , otrzymasz ulepszenia od niektórych użytkowników i programistów (a dodanie nowej opcji programu jest często jedną z najłatwiejszych rzeczy do dodania do istniejącego bezpłatnego oprogramowania). Ponadto twoje pytanie zależy w dużej mierze od zamierzonej publiczności : gra dla nastolatków lub przeglądarka dla babci prawdopodobnie nie potrzebuje tego samego rodzaju i ilości opcji niż kompilator lub inspektor sieci dla sysadminów centrum danych lub oprogramowanie CAD dla mikroprocesora architekci lub projektanci mostów. Inżynier zaznajomiony z programowaniem i skryptami prawdopodobnie lubi dużo więcej możliwości dostrajania niż twoja babcia i prawdopodobnie może chcieć móc uruchomić twoją aplikację bez X11 (być może w crontabpracy).

Basile Starynkevitch
źródło
18
Zgadzam się, ale git jest lepszym przykładem. Nie polecam nawet patrząc na cvsw 2016 roku
Basile Starynkevitch
8
+ w przypadku nieprawidłowej opcji po prostu pokaż pomoc. SOOOO denerwuje się na przykład niepotrzebnym komunikatem o błędzie dd -h.
domen
8
Programy GNU --helpz reguły obsługują, ale często nie rozpoznają, -hco jest denerwujące. Zazwyczaj piszę -h, gdy zapominam o opcji programu, denerwujące jest ponowne wpisywanie polecenia dłuższą opcją --help. W końcu napisałem -h, ponieważ coś zapomniałem. Dlaczego użytkownik powinien pamiętać, które programy wymagają „--help”, a które programy wymagają „-h”, aby wyświetlić ekran pomocy? Wystarczy dołączyć opcję zarówno -h, jak i --help.
Brandin
30
Pierwsza część tej odpowiedzi jest dobra, ale gdzieś po drodze zaczynasz trochę stycznie. Na przykład pliki konfiguracyjne, wtyczki i dlopen, wybór licencji na oprogramowanie i przeglądarki internetowe nie są już tak naprawdę powiązane z konwencjami interfejsu wiersza poleceń.
Brandin
5
I proszę. Jeśli sformatujesz dane wyjściowe na ekranie, dodaj zastąpienie formatu wyjściowego. Nic nie denerwuje mnie bardziej niż polecenia, które obcinają lub zawijają linie, gdy próbuję użyć ich w skrypcie.
Sobrique,
68

Zaletą jest fakt, że konwencja formatu danych jest popularna .

Łatwo zauważyć, że użycie = lub: lub nawet '' jako separatora to trywialne różnice, które można bez trudu przekonwertować na komputer. Jaki byłby wielki wysiłek jest dla człowieka, aby pamiętać „Teraz widać, zrobił to rzadko używane rzeczy z programu ograniczają :lub z =? Hmmm ...”

Innymi słowy, na miłość boską, nie odchodź od ekstremalnie zakorzenionych konwencji bez ważnego powodu. Ludzie zapamiętają twój program jako „ten z dziwną i irytującą składnią cmdline” zamiast „ten, który uratował mój esej z college'u”.

Kilian Foth
źródło
19
dla prawie wszystkich języków istnieją funkcje biblioteczne do obsługi argumentów wiersza poleceń. Użyj jednego z nich.
kevin cline
9
Dobra rada, ale jakie są te konwencje? -1
RubberDuck,
14
Istnieje ciekawy przykład powszechnie używanego narzędzia UNIX, które celowo narusza tę konwencję: ddużywa key=valueskładni. Powodem tej decyzji projektowej jest to, że to narzędzie (pseudonim: d ata d estroyer) może powodować duże szkody, gdy jest używane nieprawidłowo. Zmuszając użytkownika do porzucenia zwykłego nawyku, zmuszają go do dokładniejszego zastanowienia się nad tym, co robią.
Filip
18
Czy jesteś pewien, że to jest powód dd? Uważam, że został po prostu zakodowany w czasie (lata 70.?), Kiedy ARG_MAX jądra był mały, powłoki nie miały autouzupełniania i --long-argumentsnie istniały. Od tego czasu lepiej ddpozostawał kompatybilny wstecz
Basile Starynkevitch
9
ddpochodzi z innego systemu operacyjnego (niż UNIX) - języka skryptowego (JCL) w systemie operacyjnym IBM / 360 - w którym konwencje były różne, zanim zostały przeniesione mniej lub bardziej niezmienione na system UNIX - częściowo dlatego, że osoby, które prawdopodobnie będą go używać, znały go z poprzedni system.
Baard Kopperud
29

W kategoriach laika

Jeśli wejdziesz między wrony, musisz krakać jak i one.

  • Jeśli aplikacja CLI jest przeznaczona dla systemu Linux / Unix, użyj konwencji -p valuelub --parameter value. Linux ma narzędzia do łatwego analizowania takich parametrów i flag.

Zwykle robię coś takiego:

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
  • Jeśli aplikacja CLI jest przeznaczona dla systemu Windows, użyj jej /flagi /flag:valuekonwencji.

  • Jednak niektóre aplikacje, takie jak Oracle, nie korzystają z nich. Użyj narzędzi Oracle PARAMETER=VALUE.

  • Jedną z rzeczy, które lubię robić, jest oprócz akceptowania parametrów w wierszu poleceń, zapewnienie opcji używania pliku parfile , który jest plikiem pary klucz-wartość, aby uniknąć długich łańcuchów parametrów. W tym celu należy podać dodatkowy --parfile mifile.parparametr. Oczywiście, jeśli --parfilejest używany, wszystkie inne parametry są odrzucane na korzyść tego, co znajduje się w pliku.

  • Dodatkową sugestią jest zezwolenie na użycie niektórych niestandardowych zmiennych środowiskowych , na przykład ustawienie zmiennej środowiskowej MYAPP_WRKSPACE=/tmpsprawi, że zbędne będzie zawsze ustawianie --wrkspace /tmp.

  • W Linuksie nie zapomnij dodać autouzupełniania parametrów , co oznacza, że ​​użytkownicy mogą wpisać pół przełącznika, nacisnąć, TABa następnie powłoka wykona je za nich.
Tulains Córdova
źródło
1
Cóż, kilka narzędzi GNU (np. gcc) Obsługuje @mifile.partakie same --parfile mifile.parsugestie.
Basile Starynkevitch,
7
Czy na pewno zignorujesz wszystkie inne opcje, jeśli istnieje plik parametrów? W najlepszym razie wydaje mi się to sprzeczne z intuicją.
Jonathan Leffler
8
Zgadzam się z Jonathanem w sprawie plików parametrów. Jeśli plik parametrów jest obecny, oczekiwałbym, że zostanie użyty do wartości domyślnych, a argumenty podane w wierszu poleceń zostaną zastosowane nad argumentami w pliku parfile. Jeśli z jakiegoś powodu użycie pliku parafelowego wyklucza użycie dodatkowych argumentów wiersza poleceń, obecność dodatkowych argumentów powinna być błędem.
Eldritch Cheese
@EldritchCheese W aplikacjach CLI, które napisałem, gdy podany jest plik parf, dowolny dodatkowy parametr generuje błąd.
Tulains Córdova
1
Jeśli używasz zdolnej powłoki, ten rodzaj opcji „parametr pliku konfiguracyjnego” nie jest konieczny. np. po prostu piszesz fooCmd -opt1 -opt2 $(cat more_options.opts). Dlatego oczekiwałbym, że opcja „parametr pliku konfiguracyjnego”, jeśli zostanie podana, powinna zasadniczo działać w ten sam sposób.
Brandin
19

Jedna rzecz, która jeszcze się nie pojawiła:

Spróbuj zaprojektować oprogramowanie od argumentów wiersza poleceń w górę. Znaczenie:

Przed zaprojektowaniem funkcjonalności należy zaprojektować interfejs użytkownika.

Pozwoli to na wcześniejsze przeanalizowanie skrzynek i typowych skrzynek. Oczywiście nadal będziesz abstrakcjonował elementy zewnętrzne i wewnętrzne, ale da to znacznie lepszy wynik niż tylko napisanie całego kodu, a następnie zatrzaśnięcie w nim CLI.

Ponadto sprawdź docopt ( http://docopt.org/ ).

docopt jest świetną pomocą w wielu językach, szczególnie w pythonie, gdzie masz poważne ograniczenia parserów argumentów niekorzystnych dla użytkownika, takich jak argparse, wciąż uważanych za „OK”. Zamiast parserów i akapitów oraz dyktandów warunkowych, wystarczy zdefiniować pomoc dotyczącą składni, a ona zajmie się resztą.

Florian Heigl
źródło
Uwielbiam tę odpowiedź i dziękuję za nią, ponieważ obecnie jestem sfrustrowany tym, że nie jestem argparseprogramistą, interfejsem użytkownika ani przyjazny dla użytkownika.
kot
Próbowałem docopt i nie podoba mi się to. kliknięcie powoduje , że kod jest znacznie bardziej przejrzysty, aczkolwiek jest trochę chytrości w opcjach, gdy masz podkomendy.
jpmc26
2
To zbyt przypomina reklamę. Wspomnij o zasobie tylko raz, jeśli jest odpowiedni. Ale tak jak teraz, 50% twojej odpowiedzi brzmi jak promowanie zewnętrznego źródła.
Brandin
Powiedziałbym to jako „najpierw zaprojektuj model użytkowania”. Oddzielenie doświadczenia użytkownika od funkcjonalności może być sztucznym rozróżnieniem w wielu przypadkach (ograniczenia narzędzi wpływają na interfejs).
copper.hat
1
+1 dla Docopt. Rozwiązało wszystkie moje dylematy CLI, całkowicie bezbolesne. Czasami trudno jest odróżnić reklamę od prawdziwego entuzjazmu, ale oto ona - jestem entuzjastą Docopt od lat, nie jestem w żaden sposób powiązana;)
frnhr 21.01.16
3

Kilka cennych komentarzy zostało już podanych (@Florian, Basile), ale dodam ... OP mówi:

Zapewniamy interfejs front-end do zarządzania urządzeniami. Jednak niektórzy zaawansowani administratorzy mogą chcieć samodzielnie skonfigurować aplikację

Ale także uwagi:

Nie chciałem, aby to pytanie dotyczyło konkretnej platformy lub języka

Musisz wziąć pod uwagę odbiorców docelowych - zaawansowanych administratorów . Na jakiej platformie normalnie działają - Win / Unix / Mac? A na jakiej platformie działa Twoja aplikacja? Postępuj zgodnie z konwencjami CLI, które zostały już ustanowione dla tej platformy. Czy twoi „zaawansowani” administratorzy chcą / potrzebują narzędzia opartego na GUI?

Chcesz, aby interfejs był spójny wewnętrznie i z innymi narzędziami administracyjnymi. Nie chcę się zatrzymywać i myślę, że to cmd -p <arg>lub cmd -p:<arg>lub cmd /p <arg>. Czy potrzebuję cytatów, bo jest miejsce? Czy mogę cmd -p <val1> <val2>lub cmd -p <val1> -p <val2>dla wielu celów? Czy są one specyficzne dla zamówienia? Przeciążalny? Czy cmd -p2 <arg> -p1 <arg>też działa? Czy ls -l -r -t dir1 dir2== ls -trl dir1 dir2?

W przypadku narzędzi administracyjnych dla systemu Unix zawsze pamiętałem o wskazówkach dostarczonych przez Heinera Shelldorado , a także o innych wspomnianych źródłach.

Równie ważne jak projektowanie interfejsu CLI jest upewnienie się, że aplikacja jest zaprojektowana do pracy z argumentami wiersza poleceń takimi samymi jak z GUI - tj .: brak logiki biznesowej w GUI lub użycie wspólnej komendy wywoływanej zarówno z GUI, jak i CLI.

Większość narzędzi administracyjnych opartych na systemie UNIX jest faktycznie projektowanych jako narzędzia wiersza poleceń, a udostępniony interfejs GUI po prostu ułatwia „wypełnianie” opcji wiersza poleceń. Takie podejście pozwala na automatyzację, korzystanie z plików odpowiedzi itp. Oraz zarządzanie zestawem głośnomówiącym (mniej pracy dla mnie!)

Jako klasyczny zestaw narzędzi używany w / to podejście to Tcl / Tk . Nie sugeruję zmiany narzędzi; wystarczy rozważyć podejście projektowe, pisząc najpierw aplikację administracyjną opartą na graficznym interfejsie użytkownika jako narzędzie wiersza polecenia; następnie dla wygody nałóż GUI na wierzch. W pewnym momencie prawdopodobnie odkryjesz, że GUI jest uciążliwe (i podatne na błędy), jeśli musisz wykonać wiele konfiguracji i ponownie wprowadzać ogólnie te same opcje w kółko i będziesz szukać automatycznego rozwiązania.

Pamiętaj, że administrator prawdopodobnie i tak musi wpisać poprawne wartości w odpowiednich polach, więc ile wysiłku i tak odczuwasz za pomocą graficznego interfejsu użytkownika?

Ian W.
źródło
Świetnie tak! Jedno pytanie to także: czy mogę uruchomić ./this-script --hosts <hosts.txt
Florian Heigl