¿Cuáles son los buenos hábitos para diseñar argumentos de línea de comandos?

190

Mientras desarrollaba la aplicación, comencé a preguntarme: ¿cómo debo diseñar argumentos de línea de comandos?

Muchos programas están usando fórmulas como esta -argument valueo /argument value. La solución que se me ocurrió fue argument:value. Pensé que era bueno porque sin espacios en blanco no hay forma de que los valores y los argumentos puedan confundirse. También es fácil dividir una cadena en dos en el primero desde el :carácter izquierdo .

Mis preguntas son:

  1. ¿Es la -argument valuefórmula popular mejor que argument:value(más legible, más fácil de escribir, libre de errores, más fácil de entender por los desarrolladores expertos)?
  2. ¿Hay algunas reglas comúnmente conocidas que debería seguir al diseñar argumentos de línea de comandos (aparte de si funciona, está bien)?

Pidió algunos detalles más, lo proporcionaré. Sin embargo, creo que no deberían afectar las respuestas. La pregunta es sobre buenos hábitos en general. Creo que son todos iguales para todo tipo de aplicaciones.

Estamos trabajando en una aplicación que se utilizará en lugares públicos (tótems táctiles, tablas). Las aplicaciones se escriben usando Qt Quick 5 (C ++, QML, JS). Los dispositivos tendrán Windows 8.1 / 10 instalado. Proporcionaremos una interfaz frontal para administrar los dispositivos. Sin embargo, algunos administradores avanzados pueden querer configurar la aplicación por su cuenta. No es muy importante desde el lado del negocio, pero como estoy de acuerdo con lo que dijo Kilian Foth , no quiero que mi aplicación sea una molestia para un usuario. No encontré en Internet lo que quiero, pregunté aquí.


Para usuarios más avanzados de Stack Exchange: quería que esta pregunta fuera general. Tal vez califica para el wiki de la comunidad (no sé si la pregunta existente se puede convertir con respuestas). Como quiero que esta pregunta sea independiente del sistema operativo y del lenguaje de programación, las respuestas que aparecen aquí pueden ser una valiosa lección para otros desarrolladores.

Filip Hazubski
fuente
14
Mira a las populares herramientas de línea de comandos. Por ejemplo, el guión simple se usa a menudo para permitir opciones de combinación. por ejemplo, puede escribir ls -ltrpara combinar las opciones -l, -ty -r. Los programas de estilo GNU también suelen permitir opciones basadas en palabras con un guión doble como en --reverselugar de -r. Hay otras convenciones populares como -hmostrar ayuda, --señalar el final de las opciones, especificar -un nombre de archivo para permitir la lectura de stdin, etc.
Brandin
72
Tampoco -argument valueno -argument:valueson comunes. Comunes son -a value, -avaluey --argument=value.
reinierpost
39
Use una biblioteca de análisis de línea de comandos popular (generalmente llamada algo así getopt(s)) donde pueda.
reinierpost
55
@ k3b Estamos trabajando con Qt. Como dijo Kevin Cline en su comentario , podemos usar la biblioteca ya disponible. Supongo que es multiplataforma y bien pensado. QCommandLineParser
Filip Hazubski
11
El problema con su resumen final es que el análisis de argumentos no es un problema independiente de la plataforma.
pydsigner

Respuestas:

237

En los sistemas POSIX (p. Ej., Linux, MacOSX), al menos para los programas posiblemente iniciados en un terminal de shell (p. Ej., La mayoría de ellos), recomendaría usar las convenciones de codificación de GNU (que también enumeran nombres de argumentos comunes) y consultar las pautas de utilidades POSIX , incluso para software propietario:

  • siempre maneja --versiony--help (¡incluso los /bin/trueacepta!). ¡Maldigo a los autores del software que no entienden --help, los odio (porque prog --help es el primer comando que estoy probando en un nuevo programa)! A menudo --helpse puede abreviar como-h

  • Haga que el --helpmensaje enumere todas las opciones (a menos que tenga demasiadas de ellas ... en ese caso enumere las más comunes y consulte explícitamente alguna manpágina o alguna URL) y los valores predeterminados de las opciones, y quizás importantes (y específicos del programa ) Variables de entorno. Mostrar estas listas de opciones en el error de argumento de opción.

  • aceptar -ael argumento corto (de una sola letra) y tienen algún equivalente --long-argument, por lo que -a2 --long-argument=2, --long-argument 2; por supuesto, podría tener (para opciones poco utilizadas) algún --only-long-argumentnombre; para argumentos modales sin opciones adicionales -cfgeneralmente se maneja como -c -f, etc., por lo que su -argument:valuepropuesta es extraña, y no recomiendo hacerlo.

  • use GLIBC getopt_long o mejor (por ejemplo , argp_parse , en OCaml es un Argmódulo , ...)

  • a menudo se usa -para entrada o salida estándar (si no puede hacerlo, maneje /dev/stdine /dev/stdoutincluso en los pocos sistemas operativos que no los tienen)

  • imitar el comportamiento de programas similares reutilizando la mayoría de sus convenciones de opciones; en particular -npara funcionamiento en seco (à la make), -hpara ayuda, -vpara verbosidad, etc.

  • utilizar --como separador entre opciones y archivo u otros argumentos

  • si su programa usa isattypara probar que stdin es un terminal (y se comporta "interactivamente" en ese caso), proporcione una opción para forzar el modo no interactivo, del mismo modo si su programa tiene una interfaz GUI (y prueba getenv("DISPLAY")en el escritorio X11) pero también podría ser utilizado en lote o línea de comando.

  • Algunos programas (p gcc. Ej. ) Aceptan listas de argumentos indirectos, por @somefile.txtlo que significa leer los argumentos del programa somefile.txt; Esto podría ser útil cuando su programa acepte una gran cantidad de argumentos (más que los de su núcleo ARG_MAX)

Por cierto, incluso podría agregar algunas instalaciones de autocompletar para su programa y shells habituales (como basho zsh)

Algunos comandos antiguos de Unix (por ejemplo dd, o incluso sed) tienen argumentos de comando extraños para compatibilidad histórica. Recomendaría no seguir sus malos hábitos (a menos que esté haciendo una mejor variante de ellos).

Si el software es una serie de programas de línea de comandos relacionados, tomar la inspiración de GIT (que utiliza tanta seguridad como una herramienta de desarrollo), que acepta git helpy git --helpy tienen muchos gitsubcommandygitsubcommand--help

En casos excepcionales, también puede usar argv[0](mediante el uso de enlaces simbólicos en su programa), por ejemplo, bashinvocado ya que rbashtiene un comportamiento diferente ( shell restringido ). Pero generalmente no recomiendo hacer eso; podría tener sentido si su programa pudiera usarse como un intérprete de script usando shebang, es decir, #!en la primera línea interpretada por execve (2) . Si haces tales trucos, asegúrate de documentarlos, incluso en los --helpmensajes.

Recuerde que en POSIX la cáscara se globbing argumentos ( antes de ejecutar el programa!), Así que evite que requieren caracteres (como *o $o ~) en las opciones que necesitan ser cáscara escapado.

En algunos casos, puede incorporar un intérprete como GNU guile o Lua en su software (evite inventar su propio lenguaje de script completo de Turing si no es experto en lenguajes de programación). Esto tiene profundas consecuencias en el diseño de su software (¡por lo tanto, debe pensarlo pronto!). Entonces debería poder pasar fácilmente algún guión o alguna expresión a ese intérprete. Si adopta ese enfoque interesante, diseñe su software y sus primitivas interpretadas con cuidado; podrías tener algún usuario extraño que codifique grandes scripts para tu cosa.

En otros casos, es posible que desee permitir que sus usuarios avanzados carguen su complemento en su software (utilizando técnicas de carga dinámica a la dlopen& dlsym). Nuevamente, esta es una decisión de diseño muy importante (así que defina y documente la interfaz del complemento con cuidado), y deberá definir una convención para pasar las opciones del programa a estos complementos.

Si su software es algo complejo, haga que acepte algunos archivos de configuración (además o reemplazo de los argumentos del programa) y probablemente tenga alguna forma de probar (o simplemente analizar) estos archivos de configuración sin ejecutar todo el código. Por ejemplo, un agente de transferencia de correo (como Exim o Postfix) es bastante complejo, y es útil poder ejecutarlo "a medias" (por ejemplo, observar cómo maneja alguna dirección de correo electrónico dada sin enviar un correo electrónico).


Tenga en cuenta que /optiones una cosa de Windows o VMS. Sería una locura en los sistemas POSIX (porque la jerarquía de archivos se usa /como un separador de directorio y porque el shell hace el globing). Toda mi respuesta es principalmente para Linux (y POSIX).


PD: Si es posible, convierta su programa en un software gratuito , obtendrá mejoras de algunos usuarios y desarrolladores (y agregar una nueva opción de programa es a menudo una de las cosas más fáciles de agregar a un software gratuito existente). Además, su pregunta depende mucho de la audiencia prevista : un juego para adolescentes o un navegador para la abuela probablemente no necesite el mismo tipo y cantidad de opciones que un compilador, o un inspector de red para los administradores de centros de datos, o un software CAD para microprocesador arquitectos o para diseñadores de puentes. Un ingeniero familiarizado con la programación y las secuencias de comandos probablemente le guste mucho más tener muchas opciones ajustables que su abuela, y probablemente quiera ejecutar su aplicación sin X11 (tal vez en un crontabtrabajo).

Basile Starynkevitch
fuente
18
De acuerdo, pero git es un mejor ejemplo. No le recomiendo incluso mirando hacia cvsen el año 2016.
Basile Starynkevitch
8
+ en caso de opción no válida, solo muestra ayuda. Es MUY molesto obtener un mensaje de error inútil, por ejemplo, para dd -h.
Domen
8
Los programas GNU son compatibles, --helppor regla general, pero a menudo no reconocen -hcuál es molesto. Normalmente escribo -h cuando olvido una opción de un programa, es molesto tener que volver a escribir el comando con la opción más larga --help. Después de todo, escribí -h porque olvidé algo. ¿Por qué se debe esperar que el usuario recuerde qué programas requieren '--help' y qué programas requieren '-h' para mostrar la pantalla de ayuda? Solo incluya una opción para -h y --help.
Brandin el
30
La primera parte de esta respuesta es buena, pero en algún momento estás tangente. Por ejemplo, los archivos de configuración, los complementos y dlopen, las opciones de licencias de software y los navegadores web ya no están realmente relacionados con las convenciones de una interfaz de línea de comandos.
Brandin
55
Y por favor. Si formatea su salida para la pantalla, agregue una anulación de formato de salida. Nada me molesta más que los comandos que truncan o ajustan las líneas cuando intento usarlas en un script.
Sobrique
68

El hecho de que una convención de formato de datos sea popular es su ventaja.

Puede ver fácilmente que usar = o: o incluso '' como separador son diferencias triviales que la computadora podría convertir entre sí con poco esfuerzo. Lo que sería un gran esfuerzo para un ser humano es recordar "Ahora vean, ¿este programa de uso poco frecuente delimitó las cosas con :o con =? Hmmm ..."

En otras palabras, por amor a Dios, no te desvíes de las convenciones extremadamente arraigadas sin una razón convincente. La gente recordará su programa como "el que tiene la sintaxis cmdline extraña y molesta" en lugar de "el que salvó mi ensayo universitario".

Kilian Foth
fuente
19
Para casi todos los idiomas hay funciones de biblioteca para manejar argumentos de línea de comandos. Usa uno de ellos.
Kevin Cline
99
Buen consejo, pero ¿cuáles son esas convenciones? -1
RubberDuck
14
Hay un ejemplo interesante de una herramienta UNIX de uso común que viola esta convención intencionalmente: ddutiliza una key=valuesintaxis. La razón de esta decisión de diseño es que esta herramienta (apodo: d ata d estroyer) puede causar mucho daño cuando se usa incorrectamente. Al obligar al usuario a abandonar su hábito habitual, lo obligan a pensar más detenidamente sobre lo que está haciendo.
Philipp
18
¿Estás seguro de que esa es la razón dd? Creo que simplemente fue codificado en un momento (¿1970?) Cuando el ARG_MAX del kernel era pequeño, los shells no tenían autocompletados y --long-argumentsno existían. Desde entonces, mejoró ddsiendo compatible con versiones anteriores
Basile Starynkevitch el
99
ddse originó en otro sistema operativo (que no sea UNIX), un lenguaje de scripting (JCL) en el sistema operativo OS / 360 de IBM, donde las convenciones eran diferentes, antes de ser transportadas más o menos sin cambios a UNIX, en parte porque aquellos que probablemente lo usarían, lo sabían por sistema anterior
Baard Kopperud
29

En términos simples

Cuando fueres haz lo que vieres.

  • Si su aplicación CLI está destinado para Linux / Unix utilizan el -p valueo --parameter valueconvención. Linux tiene herramientas para analizar dichos parámetros y marcas de una manera fácil.

Usualmente hago algo como esto:

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
  • Si su aplicación CLI está diseñada para Windows, entonces use /flagy /flag:valueconvenciones.

  • Sin embargo, algunas aplicaciones como Oracle no usan ninguno. Utilidades de Oracle PARAMETER=VALUE.

  • Una cosa que me gusta hacer es, además de aceptar parámetros en la línea de comandos, proporcionar la opción de usar un parfile , que es un archivo de pares clave-valor para evitar largas cadenas de parámetros. Para eso, debe proporcionar un --parfile mifile.parparámetro adicional . Obviamente, si --parfilese usa, todos los demás parámetros se descartan a favor de lo que hay dentro del archivo.

  • Una sugerencia adicional es permitir el uso de algunas variables de entorno personalizadas , por ejemplo, establecer la variable de entorno MYAPP_WRKSPACE=/tmpharía innecesario establecer siempre --wrkspace /tmp.

  • En Linux, no olvide agregar el autocompletado de parámetros , lo que significa que los usuarios pueden escribir medio interruptor, presionar TABy luego Shell lo completará por ellos.
Tulains Córdova
fuente
1
Bueno, varias utilidades de GNU (por ejemplo gcc) manejan @mifile.parcomo sus --parfile mifile.parsugerencias.
Basile Starynkevitch
77
¿Está seguro de ignorar todas las demás opciones si hay un archivo de parámetros? Suena contradictorio para mí, en el mejor de los casos.
Jonathan Leffler
8
Estoy de acuerdo con Jonathan sobre los archivos de parámetros. Si el archivo de parámetros está presente, entonces esperaría que se use para valores predeterminados, con argumentos dados en la línea de comando aplicados sobre los del parfile. Si por alguna razón el uso de un parfile impide el uso de argumentos de línea de comando adicionales, entonces la presencia de argumentos adicionales debería ser un error.
Eldritch Cheese
@EldritchCheese En las aplicaciones de CLI que he escrito, cuando se proporciona un parfile, cualquier parámetro adicional genera un error.
Tulains Córdova
1
Si utiliza un shell capaz, este tipo de opción de "parámetro de archivo de configuración" no es necesario. Por ejemplo, solo escribes fooCmd -opt1 -opt2 $(cat more_options.opts). Por lo tanto, esperaría una opción de "parámetro de archivo de configuración", si se proporciona, básicamente debería funcionar de la misma manera.
Brandin
19

Una cosa que aún no surgió:

Intente diseñar su software desde los argumentos de la línea de comando hacia arriba. Sentido:

Antes de diseñar la funcionalidad, diseñe la interfaz de usuario.

Esto le permitirá profundizar en casos extremos y casos comunes desde el principio. Por supuesto, aún abstraerá el exterior y el interior, pero dará un resultado mucho mejor que simplemente escribir todo el código y luego cerrar un CLI.

Además, consulte docopt ( http://docopt.org/ ).

docopt es una gran ayuda con eso en muchos idiomas, especialmente para python donde tiene analizadores de argumentos severamente limitados y adversos para el usuario como argparse que todavía se consideran "OK". En lugar de tener analizadores y subparsers y dictados condicionales, simplemente define la ayuda de sintaxis y hace el resto.

Florian Heigl
fuente
Me encanta esta respuesta, y gracias por ello, porque actualmente estoy frustrado por argparseno ser programador, interfaz de usuario o fácil de usar.
gato
Intenté docopt, y no me gusta. el clic da como resultado un código mucho más limpio, aunque hay algunas dudas con las opciones cuando tiene subcomandos.
jpmc26
2
Esto se parece demasiado a un anuncio. Mencione el recurso solo una vez si es relevante. Pero como lo es ahora, el 50% de su respuesta solo parece promocionar un recurso externo.
Brandin
Lo diría como 'diseñe el modelo de uso primero'. Separar la experiencia del usuario de la funcionalidad puede ser una distinción artificial en muchos casos (las limitaciones de la herramienta afectan la interfaz).
copper.hat
1
+1 para Docopt. Resolvió todos mis dilemas de CLI, completamente sin dolor. A veces es difícil distinguir la publicidad de un entusiasmo genuino, pero aquí está: he sido un entusiasta de Docopt durante años, no estoy afiliado de ninguna manera;)
desde el
3

Ya se han proporcionado algunos comentarios valiosos (@Florian, Basile), pero permítanme agregar ... OP dice:

Proporcionaremos una interfaz front-end para administrar los dispositivos. Sin embargo, algunos administradores avanzados pueden querer configurar la aplicación por su cuenta

Pero también observaciones:

No quería que esta pregunta fuera específica de la plataforma o el idioma

Debe considerar su público objetivo: administradores avanzados . ¿En qué plataforma trabajan normalmente: Win / Unix / Mac? ¿Y en qué plataforma se ejecuta su aplicación? Siga las convenciones de CLI que ya se han establecido para esa plataforma. ¿Sus administradores "avanzados" quieren / necesitan una herramienta basada en GUI?

Desea que la interfaz sea coherente internamente y con otras herramientas de administración. No quiero parar y pensar si es cmd -p <arg>o cmd -p:<arg>o cmd /p <arg>. ¿Necesito citas porque hay un espacio? ¿Puedo cmd -p <val1> <val2>o cmd -p <val1> -p <val2>para múltiples objetivos? ¿Son específicos del pedido? ¿Sobrecargable? ¿ cmd -p2 <arg> -p1 <arg>Funciona también? ¿ ls -l -r -t dir1 dir2== ls -trl dir1 dir2?

Para mis herramientas de administración de Unix, siempre he tenido en cuenta la orientación proporcionada por Shelldorado de Heiner junto con las otras referencias mencionadas.

Tan importante como diseñar la CLI es asegurarse de que su aplicación esté diseñada para funcionar con argumentos de línea de comandos de la misma manera que desde la GUI, es decir: no hay lógica de negocios en la GUI o utiliza el comando común llamado desde la GUI y la CLI.

La mayoría de las herramientas administrativas basadas en UNIX en realidad están diseñadas primero como herramientas de líneas de comando y la GUI proporcionada simplemente facilita "llenar" las opciones para la línea de comando. Ese enfoque permite la automatización, el uso de archivos de respuestas, etc. y la administración sin intervención (¡menos trabajo para mí!)

Como conjunto de herramientas clásico utilizado con este enfoque es Tcl / Tk . No sugiero que cambie de herramientas; solo considere primero el enfoque de diseño al escribir una aplicación administrativa basada en GUI como una herramienta de línea de comando; luego coloque la GUI en la parte superior para mayor comodidad. En algún momento, es probable que descubra que la GUI es una tarea difícil (y propensa a errores) si tiene que hacer varias configuraciones y volver a ingresar generalmente las mismas opciones una y otra vez y buscará un enfoque automatizado.

Recuerde que su administrador probablemente tenga que escribir los valores correctos en los cuadros correctos de todos modos, entonces, ¿cuánto esfuerzo está aliviando de todos modos con GUI?

Ian W
fuente
Genial si! Una pregunta también es: ¿puedo ejecutar ./this-script --hosts <hosts.txt
Florian Heigl el