Complicación de los comandos de la consola, 1979-2020

Mi pasatiempo es abrir la "Filosofía UNIX" de McIlroy en un monitor mientras leo maná en otro.

El primero de los principios de McIlroy a menudo se reformula como "Haz una cosa, pero hazlo bien". Esta es una abreviatura de sus palabras: “Cree programas que hagan una cosa bien. Para un nuevo trabajo, cree nuevos programas, en lugar de complicar los viejos agregando nuevas "funciones".

McIlroy da un ejemplo:

Parece extraño para los extraños que los compiladores de UNIX no emitan listados: la impresión se realiza mejor y se configura de manera más flexible utilizando un programa separado.

Si abre la ayuda para ls, entonces comienza con

ls [-ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1] [file ...]

Es decir, los indicadores de una letra para lsincluyen todas las letras minúsculas, excepto {jvyz}14 letras mayúsculas, @y 1. Esto es 22 + 14 + 2 = 38 solo opciones de un solo carácter.

En Ubuntu 17, la ayuda para lsno mostrará un resumen normal, pero verá que lshay 58 opciones (incluyendo --helpy --version).

Veamos si esta es una situación única o una situación normal. Hagamos una lista de algunos comandos comunes, ordenados por frecuencia de uso.



La tabla muestra el número de opciones de línea de comandos para varios comandos v7 Unix (1979), slackware 3.1 (1996), ubuntu 12 (2015) y ubuntu 17 (2017). Cuantos más parámetros, más oscuras serán las células (en una escala logarítmica).

Vemos que a lo largo de los años, el número de opciones aumenta dramáticamente: como regla, los registros se oscurecen de izquierda a derecha (más opciones), y no hay casos en que los registros se vuelvan más claros (menos opciones).

McIlroy ha condenado durante mucho tiempo el aumento en la cantidad de opciones, tamaño y funcionalidad general de los equipos 1:

, , Linux … []. , , . , , … Unix : « ? ?» , - , . , , , . … , .

Irónicamente, una de las razones del creciente número de opciones de línea de comandos es el otro dicho de McIlroy: "Escribe programas para procesar secuencias de texto porque es una interfaz universal" (ver lscomo ejemplo).

Si se transmitieron datos u objetos estructurados, el formateo puede dejarse en la etapa final. Pero en el caso del texto plano, el formato y el contenido son mixtos; Dado que el formateo solo se puede realizar analizando el contenido, los comandos generalmente agregan opciones de formato para mayor comodidad. Además, el formato se puede realizar cuando un usuario aplica su conocimiento de la estructura de datos, y "codifica" el conocimiento de los argumentos a favor de cut, awk,sedetc. (el usuario también usa su conocimiento de cómo funcionan estos programas con el formateo, porque es diferente para diferentes programas, por lo que el usuario debe saber, por ejemplo, cómo cut -f4 difiere de awk '{ print $4 }2) Esto es mucho más complicado que pasar uno o dos argumentos al siguiente comando en una secuencia, y esto transfiere la complejidad de la herramienta al usuario.

Las personas a veces dicen que no quieren admitir datos estructurados, porque en una herramienta universal uno debería admitir varios formatos. Pero ya tienen que soportar varios formatos para hacer una herramienta universal. Algunos comandos estándar no pueden leer la salida de otros comandos porque usan formatos diferentes. Por ejemplo, wc -wUnicode no lo maneja correctamente, etc. Decir que "texto" es un formato universal es lo mismo que decir que "binario" es un formato universal.

Se dice que no hay alternativa para tal complicación de las herramientas de línea de comandos. Pero las personas que dicen eso nunca han probado una alternativa, algo como PowerShell. Tengo muchas quejas sobre PowerShell, pero la transferencia de datos estructurados y la capacidad de trabajar fácilmente con datos estructurados sin tener que mantener metadatos en mi cabeza para poder transferirlos a las herramientas de línea de comandos correctas en los lugares correctos en la tubería no es una de mis quejas 3.

Cuando se le dice que los programas deben ser simples y compatibles al procesar texto, estas personas fingen que los datos de texto no tienen una estructura para analizar 4. En algunos casos, podemos pensar en todo como una fila, separados por espacios, o como una tabla con separadores de fila y columna ( con un comportamiento que, por supuesto, no es consistente con otras herramientas ). Esto agrega un poco de problemas. Todavía hay casos en los que la serialización de datos en un formato de texto sin formato agrega una complejidad significativa, ya que debido a la estructura de datos, la serialización simple al texto posteriormente requiere esfuerzos de análisis significativos para volver a asimilar los datos de manera significativa.

Otra razón por la cual los equipos ahora tienen más opciones es porque las personas han agregado indicadores convenientes para la funcionalidad que podría implementarse mediante una tubería de varios equipos. Esta práctica ha estado sucediendo desde Unix v7, donde enlsapareció una opción para cambiar el orden de clasificación (aunque esto podría hacerse pasando la salida a tac).

Con el tiempo, por pura conveniencia, se agregaron parámetros adicionales. Por ejemplo, un comando mvque originalmente no tenía parámetros ahora puede mover el archivo y crear simultáneamente una copia de respaldo (tres opciones; dos formas diferentes de especificar una copia de respaldo, una de las cuales toma un argumento y la otra no toma argumentos, lee un argumento implícito de una variable de entorno VERSION_CONTROL; otra opción le permite anular el sufijo de copia de seguridad predeterminado). Ahora mvtodavía hay opciones para nunca sobrescribir archivos o sobrescribir solo archivos más nuevos.

mkdirotro programa que anteriormente no tenía opciones. Hoy en día, todos sus indicadores, con la excepción de las opciones de seguridad para SELinux o SMACK, así como los números de ayuda y versión, se agregan solo por conveniencia: establecer permisos para el nuevo directorio y crear directorios principales si no existen.

Al tailprincipio solo había una opción -number, que indicaba el punto de partida para el trabajo. Luego agregamos tanto el formato como las opciones para la conveniencia del formato. La bandera -zreemplaza el separador de línea con null. Aquí hay otros ejemplos de opciones agregadas para mayor comodidad: -fimprimir cuando aparezcan nuevos cambios, -sestablecer el intervalo de tiempo de espera entre la comprobación de cambios / código> -f, y también -retryvolver a intentar acceder al archivo si no está disponible.

McIlroy critica a los desarrolladores por agregar todas estas opciones, pero personalmente me siento mejor. Aunque nunca he usado algunos de ellos, rara vez he usado otros, pero esta es la belleza de los parámetros de la línea de comandos: a diferencia de la interfaz gráfica, agregar estos parámetros no satura la interfaz. Sí, el maná y la ayuda aumentan, pero en la era de Google y Stackoverflow, en cualquier caso, muchos solo buscan en Google cualquier pregunta, en lugar de leer maná.

Por supuesto, agregar opciones aumenta la carga en los mantenedores. Pero este es un pago justo por los beneficios que traen. Dada la proporción del número de mantenedores y usuarios, es lógico colocar una carga adicional en el primero, y no en el segundo. Esto es similar al comentario de Gary Bernhardt de que es prudente ensayar una actuación 50 veces. Si la audiencia es de 300 personas, la relación entre el tiempo dedicado a ver la actuación y el tiempo dedicado a los ensayos seguirá siendo de 6: 1. Para las herramientas de línea de comandos populares, esta relación es aún más extrema.

Algunos podrían argumentar que todas estas opciones adicionales suponen una carga adicional para los usuarios. Esto no está del todo mal, pero esta carga de complejidad siempre existirá. La pregunta es dónde exactamente. Si imagina que un conjunto de herramientas de línea de comandos junto con un shell forman un lenguaje en el que todos pueden escribir un nuevo método, y en caso de popularidad, el método se agrega efectivamente a la biblioteca estándar, y los estándares están determinados por axiomas como "Escribir programas para procesar secuencias de texto, porque es universal interfaz ”, entonces el lenguaje se convertirá en un caos incoherente de solo escritura, si lo toma en su totalidad. Al menos, gracias a las herramientas con una amplia gama de opciones y funcionalidades, los usuarios de Unix pueden reemplazar un conjunto gigante de herramientas extremadamente inconsistentes con solo un gran conjunto de herramientas que,Aunque son inconsistentes entre sí desde el exterior, tienen cierta consistencia interna.

McIlroy implica una falta de consideración en la caja de herramientas. Como, los padres fundadores de Unix deberían sentarse en la misma habitación y pensar cuidadosamente hasta que se les ocurriera un conjunto de herramientas secuenciales de "extraordinaria simplicidad". Pero no escalará, la filosofía de Unix en sí misma inevitablemente condujo al desastre en el que estamos. No es que alguien no haya pensado mucho o mucho. El punto es una filosofía que no escala más allá de un equipo relativamente pequeño con una comprensión cultural común que puede caber en una habitación.

Si alguien quiere escribir una herramienta basada en la "filosofía de Unix", entonces diferentes personas tendrán diferentes opiniones sobre lo que significa "simplicidad" o el principio de "hacer una cosa" 5cómo la herramienta debería funcionar correctamente, y la inconsistencia florecerá en un color exuberante, como resultado de lo cual obtendrá una enorme complejidad, similar a lenguajes muy inconsistentes como PHP. La gente se burla de PHP y JavaScript por varias rarezas e inconsistencias, pero como un lenguaje y una biblioteca estándar, cualquier shell popular con una colección de herramientas populares * nix, en conjunto, es mucho peor y contiene mucha más complejidad aleatoria debido a inconsistencias incluso dentro de la misma distribución de Linux . No puede ser de otra manera. Si compara las distribuciones de Linux, BSD, Solaris, AIX, etc., la cantidad de complejidad aleatoria que los usuarios deben tener en cuenta al cambiar de sistema, eclipsa la inconsistencia de PHP o JavaScript.Los lenguajes de programación más ridiculizados son ejemplos reales de gran diseño en comparación con ellos.

Para mayor claridad, no estoy diciendo que yo o alguien más podría hacer frente mejor al desarrollo en los años 70, teniendo en cuenta el conocimiento disponible en ese momento, y crear un sistema que sería útil en ese momento y elegante en la actualidad. Por supuesto, es fácil mirar hacia atrás y encontrar problemas en retrospectiva. Simplemente no estoy de acuerdo con los comentarios de algunos conocedores de Unix, como McIlroy, que insinúan que hemos olvidado o no entendemos el valor de la simplicidad. O Ken Thompson, quien dice que C es un lenguaje tan seguro como cualquier otro, y si no queremos que aparezcan errores, solo tenemos que escribir código sin errores. Los comentarios de este tipo implican que poco ha cambiado con los años. Supuestamente, en los años 70 construimos sistemas de la misma manera que hoy, y durante cinco décadas de experiencia colectiva, decenas de millones de años-persona no nos enseñaron nada. Y si nos volvemos a los orígenes, a los creadores de Unix, entonces todo estará bien. Con el debido respeto, no estoy de acuerdo.

Aplicación: memoria


Aunque las quejas de McIlroy sobre los archivos binarios hinchados están un poco fuera del alcance de este artículo, notaré que en 2017 compré un Chromebook con 16 GB de RAM por $ 300. El binario de 1 megabyte podría ser un problema grave en 1979, cuando el Apple II estándar estaba equipado con 4 kilobytes de memoria. Apple II costó $ 1,298 en 1979, o $ 4,612 en 2020. Hoy puede comprar una Chromebook económica que cuesta menos de 1/15 de este precio, mientras que tiene cuatro millones de veces más memoria. Las quejas de que el uso de la memoria ha crecido mil veces parece un poco ridículo cuando (¡portátil!) La máquina cuesta un orden de magnitud más barato y tiene cuatro millones de veces más memoria.

Me gusta la optimización, por lo que reduje mi página de inicio a dos paquetes (habría uno si el CDN admitiera brotli de alto nivel), pero este es un requisito puramente estético, lo hago por diversión. El cuello de botella de las herramientas de línea de comandos no está usando memoria, y el tiempo para optimizar la memoria de la herramienta con un tamaño de un megabyte es como reducir una página de inicio a un paquete. Quizás un pasatiempo divertido, pero nada más.

Metodología de compilación de tablas


La frecuencia de uso de los comandos se obtiene de los archivos públicos del historial de comandos en github, no necesariamente corresponde a su experiencia personal. Solo se contaron los comandos "simples", excluyendo instancias como curl, git, gcc (este último tiene más de 1000 opciones) y wget. El concepto de simplicidad es relativo. Los comandos de shell integrados , como cd, tampoco se tuvieron en cuenta.

La repetición de banderas no se consideraba una opción separada. Por ejemplo, u git blame -C, git blame -C -Cy git blame -C -C -Ccomportamiento diferente, pero todos se considerarán como un argumento, aunque -C -Cen -C realidad son argumentos diferentes.

Las subopciones en la tabla se cuentan como una opción. Por ejemplo, lsadmite lo siguiente: a

--format=WORD across -x, commas -m, horizontal -x, long -l, single-column -1, verbose -l, vertical -C

pesar de siete opciones format, esto cuenta como una opción.

Las opciones que se indican explícitamente como inútiles todavía se consideran opciones, por ejemplo ls -g, lo que se ignora también se considera.

Varias versiones de la misma opción se consideran una opción. Por ejemplo, -Ay --almost-allpara ls.

Si el certificado dice que la opción existe, pero en realidad no existe, entonces no se tiene en cuenta. Por ejemplo, la ayuda para v7 mv dice:

BOLSAS

Si el archivo1 y el archivo2 están en sistemas de archivos diferentes, entonces mv debería copiar el archivo y eliminar el original. En este caso, el nombre del propietario se convierte en el nombre del proceso de copia y se pierde cualquier conexión con otros archivos.

mv debe aceptar el indicador -f como rm para suprimir un mensaje sobre la existencia de un archivo de destino que no se puede escribir.

Pero -fno se considera una bandera en la tabla, porque la opción no existe realmente.

La tabla finaliza en 2017 porque luego se escribió el primer borrador de este artículo. Solo ahora llegaron a leerlo.

Sobre este tema



, , -, //.



1. Esta cita es ligeramente diferente de la versión común porque vi el video original . Por lo que puedo decir, todas las copias de esta cita en Internet (índices Bing, DuckDuckGo y Google) se toman de la misma transcripción de una persona. Hay una cierta ambigüedad, porque el sonido es de baja calidad, y escucho palabras que son ligeramente diferentes de lo que esa persona escuchó. [regresar]

2. Otro ejemplo de cómo la complejidad se transmite al usuario, porque los diferentes equipos manejan el formateo de manera diferente, es el formateo de tiempo . El tiempo de shell incorporado es time, por supuesto, incompatible con /usr/bin/time. El usuario debe ser consciente de este hecho y saber cómo manejarlo. [regresar]

3. Por ejemplo, para cualquier objeto que pueda usar ConvertTo-Jsono ConvertTo-CSV. O "cmdlets" para cambiar la visualización de las propiedades del objeto . Puede escribir archivos de configuración de formato que definan sus métodos de formato preferidos.

Otra forma de ver esto es a través del prisma de la ley de Conway . Si tenemos un conjunto de herramientas de línea de comandos creadas por diferentes personas, a menudo de diferentes organizaciones, estas herramientas serán muy inconsistentes si alguien no puede determinar el estándar y obligar a las personas a aceptarlo. En realidad, esto funciona relativamente bien en Windows, no solo en PowerShell.

Una queja común contra Microsoft es la rotación masiva de la API, a menudo por razones organizativas no técnicas (por ejemplo, vea las acciones de Stephen Sinofsky como se describe en las respuestas a un tweet remoto ). Es verdad. Sin embargo, desde el punto de vista de un usuario ingenuo, el software estándar de Windows, por regla general, transmite datos no textuales mucho mejor que * nix. La cobertura de datos no textuales en Windows se remonta al menos a COM en 1999 (y posiblemente a OLE y DDE, lanzados en 1990 y 1987, respectivamente).

Por ejemplo, si copia desde Foo, que admite el formato binario Ay Ben la barra, que admite formatos, By Cluego copia desde Bar a Baz, que admite CyD, todo funcionará bien, incluso si Foo y Baz no tienen formatos compatibles comunes.

Cuando corta o copia algo, la aplicación básicamente "le dice" al portapapeles en qué formatos puede proporcionar datos. Cuando se pega en una aplicación, la aplicación final puede solicitar datos en cualquiera de los formatos disponibles. Si los datos ya están en el portapapeles, Windows lo proporciona. Si este no es el caso, Windows recibe datos de la aplicación de origen y luego los transfiere a la aplicación de destino, y una copia se almacena durante algún tiempo. Si "corta" de Excel, él dirá "usted" que tiene datos disponibles en muchas docenas de formatos. Tal sistema es bastante bueno para la compatibilidad, aunque ciertamente no puede llamarse simple o minimalista.

Además del buen soporte de muchos formatos, es lo suficientemente largo como para que muchos programas comiencen a manejar bien estas características; en Windows, por lo general, hay un buen soporte para el portapapeles.

Suponga que copia y pega una pequeña cantidad de texto. En la mayoría de los casos, no habrá sorpresas ni en Windows ni en Linux. Pero ahora suponga que copió algún texto, cerró el programa desde el que copió y luego lo pegó. Muchos usuarios tienden a pensar que cuando se copian datos se almacenan en el portapapeles y no en el programa desde el que se copian. En Windows, el software generalmente se escribe de acuerdo con esta expectativa (aunque técnicamente los usuarios de la API del portapapeles no deberían hacer esto). Esto es menos común en Linux con X, donde el modelo mental correcto para la mayoría de los programas es que la copia guarda un puntero a los datos que aún pertenecen al programa desde el que se está copiando. Es decir, la inserción no funcionará si el programa está cerrado.Cuando entrevisté (informalmente) a los programadores, generalmente se sorprendieron de esto, a menos que realmente trabajaran con la función copiar + pegar para su aplicación. Cuando entrevisté a personas que no son programadores, generalmente encontraron este comportamiento no solo sorprendente, sino también confuso.

El inconveniente de transferir el portapapeles al sistema operativo es que copiar grandes cantidades de datos es costoso. Suponga que copia una gran cantidad de texto, muchos gigabytes o algún tipo de objeto complejo, y luego no lo pega. De hecho, no desea copiar estos datos de su programa al sistema operativo para que estén almacenados allí y disponibles. Windows lo hace sabiamente: las aplicaciones solo pueden proporcionar datos a pedido , si se consideran beneficiosos. En nuestro caso, cuando el usuario cierra el programa, puede determinar si colocar datos en el portapapeles o eliminarlos. En este caso, muchos programas (por ejemplo, Excel) ofrecerán "guardar" datos en el portapapeles o eliminarlos, lo cual es bastante razonable.

Algunas de estas características se pueden implementar en Linux. Por ejemplo,La especificación ClipboardManager describe el mecanismo de guardado, y las aplicaciones de GNOME generalmente lo admiten (aunque con algunos errores ), pero la situación en * nix realmente difiere del soporte ubicuo para las aplicaciones de Windows, donde generalmente se implementa un portapapeles competente. [regresar]

4. Otro ejemplo son las herramientas sobre los compiladores modernos. Volvamos y veamos el ejemplo canónico de Macilroy, donde los compiladores de Unix correctos son tan especializados que el listado se realiza mediante una herramienta separada. Pero hoy esto ha cambiado, aunque se ha mantenido una herramienta de listado separada. Algunos compiladores de Linux populares tienen literalmente miles de opciones, y son extremadamente ricos en funciones. Por ejemplo, una de las muchas funciones de lo moderno clang es el análisis estático. Al momento de escribir este artículo, hay 79 pruebas de análisis estático de rutina y 44 pruebas experimentales.. Si se tratara de comandos separados, seguirían dependiendo de la misma infraestructura básica del compilador e impondrían la misma carga de mantenimiento; en realidad, no es razonable que estas herramientas de análisis estático trabajen con texto plano y redefinan toda la cadena de herramientas del compilador necesaria consiguiendo el punto donde pueden realizar análisis estáticos. Pueden ser comandos separados en lugar de combinarse en clang, pero aún dependerán del mismo mecanismo e impondrán la carga de mantenimiento y complejidad en el compilador (que debería admitir interfaces estables para herramientas que funcionan sobre él), o serán constantes rotura.

Hacer todo en el texto por simplicidad suena hermoso, pero de hecho, la representación textual de los datos a menudo no es lo que necesita si desea hacer un trabajo realmente útil.

El mismo sonido metálico es simplemente más funcional que cualquier compilador que existió en 1979, o incluso todos los compiladores que existieron en 1979 combinados, independientemente de si fue ejecutado por un comando monolítico o miles de instrucciones más pequeñas. Es fácil decir que en 1979 todo fue más simple y que nosotros, los programadores modernos, nos extraviamos. Pero en realidad es difícil ofrecer un diseño que sea mucho más simple y que sea verdaderamente aceptado por todos. Es imposible que dicho diseño pueda preservar toda la funcionalidad y configurabilidad existentes y ser tan simple como algo de 1979. [regresar]

5. Desde su inicio, curl ha pasado de admitir tres protocolos a 40. ¿Significa esto que "hace 40 cosas", y la filosofía de Unix requiere que se divida en 40 comandos separados? Depende de a quién preguntar. Si cada protocolo fuera su propio equipo, creado y respaldado por otra persona, tendríamos el mismo desastre que con los equipos. Parámetros inconsistentes de la línea de comandos, formatos de salida inconsistentes, a pesar del hecho de que todos estos son flujos de texto, etc. ¿Esto nos acerca a la simplicidad que aboga McIlroy? Depende de a quién preguntar. [regresar]

All Articles