Quali sono le buone abitudini per la progettazione di argomenti da riga di comando?


190

Durante lo sviluppo dell'applicazione ho iniziato a chiedermi: come devo progettare gli argomenti della riga di comando?

Molti programmi usano una formula come questa -argument valueo /argument value. La soluzione che mi è venuta in mente è stata argument:value. Ho pensato che fosse buono perché senza spazi bianchi non è possibile confondere valori e argomenti. Inoltre è facile dividere una stringa in due sul primo dal :carattere di sinistra .

Le mie domande sono:

  1. La -argument valueformula popolare è migliore di argument:value(più leggibile, più facile da scrivere, senza errori, più facile da capire da sviluppatori esperti)?
  2. Ci sono alcune regole comunemente note che dovrei seguire durante la progettazione degli argomenti della riga di comando (a parte se funziona, va bene)?

Chiesto qualche dettaglio in più lo fornirò. Tuttavia, penso che non dovrebbero influenzare le risposte. La domanda riguarda le buone abitudini in generale. Penso che siano tutti uguali per tutti i tipi di applicazioni.

Stiamo lavorando a un'applicazione che verrà utilizzata in luoghi pubblici (touch totem, tabelle). Le applicazioni vengono scritte usando Qt Quick 5 (C ++, QML, JS). Sui dispositivi sarà installato Windows 8.1 / 10. Forniremo un'interfaccia front-end per gestire i dispositivi. Tuttavia, alcuni amministratori avanzati potrebbero voler configurare l'applicazione da soli. Non è molto importante dal punto di vista aziendale, ma poiché sono d'accordo con quanto affermato da Kilian Foth , non voglio che la mia domanda sia dolorosa per un utente. Non trovando su Internet quello che voglio, ho chiesto qui.


Agli utenti più avanzati di Stack Exchange: volevo che questa domanda fosse generale. Forse si qualifica per il wiki della comunità (non so se la domanda esistente può essere convertita con le risposte). Poiché voglio che questa domanda sia indipendente dal sistema operativo e dal linguaggio di programmazione, le risposte che appaiono qui possono essere una lezione preziosa per altri sviluppatori.


14
Guarda i popolari strumenti da riga di comando. Ad esempio, il singolo trattino viene spesso utilizzato per consentire la combinazione di opzioni. ad esempio, si potrebbe scrivere ls -ltrper combinare le opzioni -l, -te -r. I programmi in stile GNU in genere consentono anche opzioni basate su parole con un doppio trattino come --reverseinvece di -r. Esistono altre convenzioni popolari come -hmostrare aiuto, --segnalare la fine delle opzioni, specificando -come nome file per consentire la lettura da stdin, ecc.
Brandin

72
-argument valuenon -argument:valuesono comuni. Comuni sono -a value, -avaluee --argument=value.
reinierpost,

39
Usa una popolare libreria di analisi della riga di comando (di solito chiamata qualcosa di simile getopt(s)) dove puoi.
reinierpost,

5
@ k3b Stiamo lavorando con Qt. Come diceva kevin cline nel suo commento , possiamo usare la libreria già disponibile. Suppongo che sia multipiattaforma e ben pensato. QCommandLineParser
Filip Hazubski,

11
Il problema con il tuo blurb finale è che l'analisi dell'argomento non è un problema indipendente dalla piattaforma.
pydsigner,

Risposte:


237

Sui sistemi POSIX (ad es. Linux, MacOSX), almeno per i programmi eventualmente avviati in un terminale shell (ad es. La maggior parte di essi), consiglierei di utilizzare le convenzioni di codifica GNU (che elenca anche nomi di argomenti comuni) e di consultare le linee guida sulle utility POSIX , anche per software proprietario:

  • gestisci sempre --versione--help (anche /bin/trueli accetta !!). Maledico gli autori di software che non capiscono --help, li odio (perché prog --help è il primo comando che sto provando su un nuovo programma)! Spesso --helppuò essere abbreviato come-h

  • Chiedi al --helpmessaggio di elencare tutte le opzioni (a meno che tu non ne abbia troppe ... in quel caso elenca le più comuni e fai esplicito riferimento a qualche manpagina o qualche URL) e ai valori predefiniti delle opzioni, e forse importanti (e specifici del programma ) variabili ambientali. Mostra questi elenchi di opzioni sull'errore dell'argomento opzione.

  • accettare -abreve argomento (singola lettera) e avere qualche equivalente --long-argument, così -a2 --long-argument=2, --long-argument 2; ovviamente potresti avere (per opzioni usate raramente) un --only-long-argumentnome; per argomenti modali senza opzioni extra -cfè generalmente gestito come -c -f, ecc. quindi la tua -argument:valueproposta è strana e non ti consiglio di farlo.

  • usa GLIBC getopt_long o meglio (es. argp_parse , in OCaml è il Argmodulo , ...)

  • spesso usano -per input o output standard (se non puoi farlo, gestisci /dev/stdine /dev/stdoutanche sui pochi sistemi operativi che non li hanno)

  • imitare il comportamento di programmi simili riutilizzando la maggior parte delle convenzioni sulle opzioni; in particolare -nper la corsa a secco (à la make), -hper aiuto, -vper verbosità, ecc ...

  • utilizzare --come separatore tra opzioni e file o altri argomenti

  • se il tuo programma usa isattyper testare che stdin è un terminale (e si comporta "interattivamente" in quel caso), fornisci un'opzione per forzare la modalità non interattiva, allo stesso modo se il tuo programma ha un'interfaccia GUI (e test getenv("DISPLAY")sul desktop X11) ma potrebbe anche essere utilizzato in batch o riga di comando.

  • Alcuni programmi (ad es. gcc) Accettano liste di argomenti indiretti, quindi @somefile.txtsignifica leggere argomenti del programma da somefile.txt; questo potrebbe essere utile quando il tuo programma potrebbe accettare molti argomenti (più di quelli del tuo kernel ARG_MAX)

A proposito, potresti persino aggiungere alcune funzionalità di completamento automatico per il tuo programma e le solite shell (come basho zsh)

Alcuni vecchi comandi Unix (ad esempio dd, o anche sed) hanno strani argomenti di comando per la compatibilità storica. Consiglierei di non seguire le loro cattive abitudini (a meno che non ne stiate facendo una variante migliore).

Se il tuo software è una serie di programmi a riga di comando correlati, prendi ispirazione da git (che sicuramente utilizzerai come strumento di sviluppo), che accetta git helpe git --helpche ha molti gitsubcommandegitsubcommand--help

In rari casi potresti anche usare argv[0](usando i collegamenti simbolici sul tuo programma), ad esempio bashinvocato perché rbashha un comportamento diverso ( shell limitata ). Ma di solito non consiglio di farlo; potrebbe avere senso se il tuo programma potesse essere usato come interprete di script usando shebang, cioè #!sulla prima riga interpretata da execve (2) . Se fai questi trucchi, assicurati di documentarli, anche nei --helpmessaggi.

Si ricorda che sul POSIX la shell è globbing argomenti ( prima esecuzione del programma!), In modo da evitare che richiedono caratteri (come *o $o ~) nelle opzioni, che avrebbe bisogno di essere shell escape.

In alcuni casi, potresti incorporare un interprete come GNU guile o Lua nel tuo software (evita di inventare il tuo linguaggio di scripting completo di Turing se non sei esperto in linguaggi di programmazione). Ciò ha profonde conseguenze sulla progettazione del tuo software (quindi dovresti pensarci presto!). Dovresti quindi essere in grado di passare facilmente qualche script o espressione a quell'interprete. Se segui questo approccio interessante, progetta con cura il tuo software e le sue primitive interpretate; potresti avere degli strani utenti che codificano grandi script per te.

In altri casi, potresti voler consentire ai tuoi utenti esperti di caricare i loro plugin nel tuo software (usando tecniche di caricamento dinamico à la dlopen& dlsym). Ancora una volta, questa è una decisione di progettazione molto importante (quindi definisci e documenta con cura l'interfaccia del plug-in) e dovrai definire una convenzione per passare le opzioni del programma a questi plugin.

Se il tuo software è una cosa complessa, fallo accettare alcuni file di configurazione (in aggiunta o in sostituzione degli argomenti del programma) e probabilmente avrai un modo per testare (o semplicemente analizzare) questi file di configurazione senza eseguire tutto il codice. Ad esempio, un agente di trasferimento di posta (come Exim o Postfix) è piuttosto complesso, ed è utile essere in grado di "eseguirlo a metà" (ad es. Osservando come sta gestendo un determinato indirizzo e-mail senza inviare effettivamente una e-mail).


Si noti che /optionè una cosa Windows o VMS. Sarebbe folle sui sistemi POSIX (perché la gerarchia di file utilizza /come separatore di directory e perché la shell fa il globbing). Tutta la mia risposta è principalmente per Linux (e POSIX).


PS Se possibile, rendi il tuo programma un software gratuito , otterrai miglioramenti da alcuni utenti e sviluppatori (e l'aggiunta di una nuova opzione di programma è spesso una delle cose più facili da aggiungere a un software gratuito esistente). Inoltre, la tua domanda dipende molto dal pubblico previsto : un gioco per adolescenti o un browser per la nonna probabilmente non richiedono lo stesso tipo e la stessa quantità di opzioni di un compilatore o un ispettore di rete per amministratori di sistemi di dati o un software CAD per microprocessore architetti o per progettisti di ponti. Un ingegnere che ha familiarità con la programmazione e gli script probabilmente ama molto di più avere molte opzioni sintonizzabili rispetto a tua nonna, e probabilmente potrebbe voler essere in grado di eseguire l'applicazione senza X11 (forse in un crontablavoro).


18
D'accordo, ma git è un esempio migliore. Non vi consiglio anche cercando in cvsnel 2016.
Basile Starynkevitch

8
+ in caso di opzione non valida, mostra solo aiuto. È così fastidioso ottenere un messaggio di errore inutile per esempio dd -h.
domen,

8
I programmi GNU supportano --helpdi norma ma spesso non riconoscono -hquale è fastidioso. Di solito digito -h quando dimentico un'opzione per un programma, è fastidioso dover digitare nuovamente il comando con l'opzione più lunga --help. Dopotutto, ho digitato -h perché ho dimenticato qualcosa. Perché ci si dovrebbe aspettare che l'utente ricordi quali programmi richiedono '--help' e quali programmi richiedono '-h' per mostrare la schermata di aiuto? Includi solo un'opzione per -h e --help.
Brandin,

30
La prima parte di questa risposta è buona, ma da qualche parte lungo una sorta di tangente. Ad esempio, file di configurazione, plug-in e dlopen, scelte di licenze software e browser Web non sono più realmente correlati alle convenzioni di un'interfaccia a riga di comando.
Brandin,

5
E per favore Se si formatta l'output per lo schermo, aggiungere una sostituzione del formato di output. Nulla mi infastidisce più dei comandi che troncano o avvolgono le linee quando sto cercando di usarli in uno script.
Sobrique,

68

Il fatto che una convenzione sul formato dei dati sia popolare è il suo vantaggio.

Puoi facilmente vedere che usare = o: o anche '' come separatore sono differenze insignificanti che possono essere convertite l'una nell'altra dal computer con il minimo sforzo. Quale sarebbe un grande sforzo per un essere umano ricordare "Ora vedi, questo programma usato di rado delimita le cose con :o con =? Hmmm ..."

In altre parole, per amore di Dio, non deviare da convenzioni estremamente radicate senza un motivo convincente. Le persone ricorderanno il tuo programma come "quello con la strana e fastidiosa sintassi cmdline" anziché "quello che ha salvato il mio saggio universitario".


19
per quasi tutte le lingue ci sono funzioni di libreria per gestire gli argomenti della riga di comando. Usane uno.
Kevin Cline il

9
Un buon consiglio, ma quali sono quelle convenzioni? -1
RubberDuck,

14
C'è un esempio interessante di uno strumento UNIX comunemente usato che viola intenzionalmente questa convenzione: ddusa una key=valuesintassi. Il motivo di questa decisione di progettazione è che questo strumento (soprannome: d ata d estroyer) può causare molti danni se usato in modo errato. Costringendo l'utente ad abbandonare la solita abitudine, costringono l'utente a riflettere più attentamente su ciò che stanno facendo.
Philipp,

18
Sei sicuro che sia la ragione dd? Credo che sia stato semplicemente codificato in un momento (anni '70?) In cui l'ARG_MAX del kernel era piccolo, le shell non avevano completamenti automatici e --long-argumentsnon esistevano. Da allora è ddrimasto meglio compatibile con le versioni precedenti
Basile Starynkevitch il

9
ddoriginato da un altro sistema operativo (rispetto a UNIX) - un linguaggio di scripting (JCL) sul sistema operativo IBM / 360 - in cui le convenzioni erano diverse, prima di essere portate più o meno invariate su UNIX - in parte perché coloro che probabilmente lo usavano, lo sapevano dal sistema precedente.
Baard Kopperud,

29

In parole povere

Paese che vai, usanze che trovi.

  • Se l'app CLI è progettata per Linux / Unix, utilizzare la convenzione -p valueo --parameter value. Linux ha strumenti per analizzare tali parametri e flag in modo semplice.

Di solito faccio qualcosa del genere:

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
  • Se l'app CLI è progettata per Windows, utilizzare /flage /flag:valueconvenzioni.

  • Alcune app come Oracle non usano né però. Utilizzo delle utility Oracle PARAMETER=VALUE.

  • Una cosa che mi piace fare è, oltre ad accettare i parametri nella riga di comando, fornire l'opzione di usare un parfile , che è un file di coppia chiave-valore per evitare lunghe catene di parametri. Per questo è necessario fornire un --parfile mifile.parparametro aggiuntivo . Ovviamente se --parfileviene utilizzato, tutti gli altri parametri vengono scartati a favore di ciò che è all'interno del file.

  • Un ulteriore suggerimento è consentire l'uso di alcune variabili d'ambiente personalizzate , ad esempio l'impostazione di una variabile d'ambiente MYAPP_WRKSPACE=/tmprenderebbe superfluo impostare sempre --wrkspace /tmp.

  • In Linux non dimenticare di aggiungere il completamento automatico dei parametri , il che significa che gli utenti possono digitare mezzo interruttore, premere TABe quindi la shell lo completerebbe per loro.

1
Bene, diverse utility GNU (es. gcc) Gestiscono @mifile.parcome i tuoi --parfile mifile.parsuggerimenti.
Basile Starynkevitch,

7
Sei sicuro di ignorare tutte le altre opzioni se c'è un file di parametri? Mi sembra controintuitivo, nella migliore delle ipotesi.
Jonathan Leffler,

8
Concordo con Jonathan sui file dei parametri. Se il file dei parametri è presente, mi aspetterei che venga utilizzato per i valori predefiniti, con gli argomenti forniti sulla riga di comando applicati sopra quelli nel file. Se per qualche motivo l'uso di un file preclude l'uso di ulteriori argomenti da riga di comando, la presenza di argomenti aggiuntivi dovrebbe essere un errore.
Eldritch Cheese,

@EldritchCheese Nelle app della CLI che ho scritto, quando viene dato un file, ogni parametro adizionale genera un errore.
Tulains Córdova,

1
Se si utilizza una shell capace, questo tipo di opzione "parametro file config" non è necessario. ad esempio, hai appena scritto fooCmd -opt1 -opt2 $(cat more_options.opts). Pertanto, mi aspetto che un'opzione "parametro file di configurazione", se fornita, dovrebbe sostanzialmente funzionare allo stesso modo.
Brandin,

19

Una cosa che non è ancora venuta in mente:

Prova a progettare il tuo software dagli argomenti della riga di comando verso l'alto. Senso:

Prima di progettare la funzionalità, progettare l'interfaccia utente.

Ciò ti consentirà di eseguire il drill down su casi limite e casi comuni all'inizio. Ovviamente continuerai ad astrarre l'esterno e l'interno, ma darà risultati molto migliori rispetto alla semplice scrittura di tutto il codice e quindi alla chiusura di una CLI.

Inoltre, controlla docopt ( http://docopt.org/ ).

docopt è di grande aiuto in molte lingue, in particolare per Python, dove si hanno ancora parecchi parser di argomenti avversi all'utente, come argparse, come "OK". Invece di avere parser, subparser e dadi condizionali, basta definire l' aiuto di sintassi e fa il resto.


Adoro questa risposta, e grazie per questo, perché attualmente sono frustrato di argparsenon essere programmatore, interfaccia utente o user friendly.
gatto

Ho provato docopt e non mi piace. un clic produce un codice molto più pulito, anche se c'è un po 'di entusiasmo con le opzioni quando si hanno sottocomandi.
jpmc26,

2
È troppo simile a una pubblicità. Menziona la risorsa solo una volta se è pertinente .. Ma com'è ora, il 50% della tua risposta sembra solo promuovere una risorsa esterna.
Brandin,

Lo definirei come "progetta prima il modello d'uso". Separare l'esperienza dell'utente dalla funzionalità può essere una distinzione artificiale in molti casi (le limitazioni degli strumenti influenzano l'interfaccia).
Copper.hat

1
+1 per Docopt. Ha risolto tutti i miei dilemmi CLI, completamente indolore. A volte è difficile distinguere la pubblicità da un autentico entusiasmo, ma eccolo qui - sono stato un appassionato di Docopt ormai da anni, non affiliato in alcun modo;)
frnhr

3

Alcuni preziosi commenti già forniti (@Florian, Basile), ma vorrei aggiungere ... OP dice,

Forniremo un'interfaccia front-end per gestire i dispositivi. Tuttavia, alcuni amministratori avanzati potrebbero voler configurare l'applicazione da soli

Ma osserva anche:

Non volevo che questa domanda fosse specifica per piattaforma o lingua

Devi considerare il tuo pubblico di destinazione - amministratori avanzati . Su quale piattaforma funzionano normalmente: Win / Unix / Mac? E su quale piattaforma esegui l'app? Seguire le convenzioni CLI già stabilite per quella piattaforma. I tuoi amministratori "avanzati" vogliono / hanno bisogno di uno strumento basato sulla GUI?

Desideri che l'interfaccia sia coerente internamente e con altri strumenti di amministrazione. Non voglio fermarmi e pensare che cmd -p <arg>sia cmd -p:<arg>o o o cmd /p <arg>. Ho bisogno di virgolette perché c'è uno spazio? Posso cmd -p <val1> <val2>o cmd -p <val1> -p <val2>per più target? Sono specifici per l'ordine? Sovraccaricabile? Funziona cmd -p2 <arg> -p1 <arg>anche tu? Does ls -l -r -t dir1 dir2== ls -trl dir1 dir2?

Per i miei strumenti di amministrazione Unix, ho sempre tenuto presente la guida fornita da Shelldorado di Heiner insieme agli altri riferimenti citati.

Importante quanto la progettazione dell'interfaccia della riga di comando è assicurarsi che l'applicazione sia progettata per funzionare con argomenti della riga di comando identici a quelli della GUI, ovvero: nessuna logica aziendale nella GUI o utilizzare il comando comune chiamato sia dalla GUI che dalla CLI.

La maggior parte degli strumenti di amministrazione basati su UNIX sono in realtà progettati prima come strumenti da riga di comando e la GUI fornita semplifica semplicemente il "popolamento" delle opzioni per la riga di comando. Tale approccio consente l'automazione, l'uso di file di risposta, ecc. E la gestione delle mani (meno lavoro per me!)

Come set di strumenti classico utilizzato con questo approccio è Tcl / Tk . Non suggerire di cambiare strumenti; considera innanzitutto l'approccio alla progettazione dalla scrittura di un'app amministrativa basata sulla GUI all'app come uno strumento da riga di comando; quindi sovrapponi la GUI per comodità. Ad un certo punto probabilmente scoprirai che la GUI è una seccatura (ed è soggetta a errori) se devi fare più configurazioni e reinserire le stesse opzioni più e più volte e cercherai un approccio automatizzato.

Ricorda che probabilmente il tuo amministratore deve digitare i valori corretti nelle caselle corrette, quindi quanti sforzi stai comunque scaricando con la GUI?


Fantastico, sì! Una domanda è anche: posso eseguire ./this-script --hosts <hosts.txt
Florian Heigl,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.