Los programas de Shell más grandes del mundo
(github.com/oils-for-unix)- Este wiki no es solo una colección de scripts largos, sino una lista de programas de Shell “sustanciales” escritos a mano que incluso usan estructuras de datos y algoritmos
- El criterio suele ser de más de 5K líneas, y se excluyen de los casos principales los scripts generados automáticamente o los scripts de completion repetitivos
- Entre los casos más representativos están ble.sh con 87K líneas, kalua con unas 56K SLoC/líneas, Relax-and-Recover con 35K líneas, nb con 26K líneas y winetricks con 22K líneas, muy por encima de la idea habitual de lo que es un script de Shell
- La lista incluye una amplia variedad de herramientas de uso real, como editores interactivos de línea, addons para OpenWRT, depuradores de Bash, herramientas de prueba TLS, implementaciones de Kubernetes, herramientas de respaldo y recuperación, herramientas de emisión de certificados y monitores de recursos
- La prueba OSH “Wild” analiza más de un millón de líneas de Shell, pero en su mayoría son programas pequeños y definiciones repetitivas de paquetes de distribuciones, por lo que se distinguen de los programas grandes de Shell
Qué se considera un “programa grande de Shell”
- Aquí “biggest” no se refiere solo al número bruto de líneas, sino a algo “substantial”, es decir, de escala y complejidad reales
- Lo incluido son, en principio, scripts de Shell escritos a mano
- Se dejan fuera como excepción los scripts grandes generados por autoconf
- Los artefactos generados automáticamente, como un script de 70K líneas de coreutils, no se consideran programas grandes de Shell en el sentido sustancial
- Se valora especialmente a los programas de Shell que usan estructuras de datos y algoritmos
- bash-completion es sofisticado, pero por su estructura repetitiva con funciones relativamente simples para cada comando en una máquina Unix, queda más cerca de un contraejemplo
- La referencia aproximada es de más de 5K líneas
- Los programas de Shell no repetitivos más grandes suelen estar en el rango de 10K+ líneas
- No se han confirmado programas que superen las 100K líneas
Ejemplos de los programas de Shell más grandes
- akinomyoga/ble.sh: 87K líneas en total, 63K LoC sin comentarios
- Es un editor interactivo de línea tipo fish escrito en bash puro
- El archivo principal
out/ble.shtiene 39K líneas, 29K LoC sin comentarios, y al sumar los archivos de módulos supera las 80K líneas en total - Tiene muchos comentarios en japonés
- Usa
bind -xpara leer raw bytes del terminal, los decodifica directamente con varias máquinas de estado explícitas y mantiene/actualiza un drawing buffer - También incluye timing y “fibers”
- Los detalles sobre su parser de Shell están en los comentarios del issue 663, y se considera uno de los casos más sofisticados de uso de estructuras de datos en Shell
- Hay intentos de ejecutarlo en OSH y en su mayor parte se puede analizar sintácticamente
- El primer commit tenía 8K líneas / 6K LoC en 2015, y el desarrollo real empezó en 2013
- kalua: addon para OpenWRT escrito en POSIX shell, con unas 56K SLoC/líneas
- Relax-and-Recover: herramienta de respaldo y recuperación con 35K líneas y 24K LoC
- El primer commit en git fue en marzo de 2009 y entonces tenía 4K líneas / 3K LoC
- xwmx/nb:
nben sí tiene 26K líneas y 22K LoC escritas en bash- Si se cuentan las pruebas de bats como bash, hay además 91K líneas y 61K LoC adicionales
- El primer commit es de 2014, y el historial de commits activo comienza a inicios de 2016
- vegardit/bash-funk: biblioteca de Bash con 27K líneas en total y 24K LoC
- El primer commit fue en mayo de 2017 y entonces tenía 10K líneas / 8K LoC
- winetricks: script de Shell de 22K líneas que instala varios programas de Windows sobre Wine
- drwetter/testssl.sh: contiene 21K líneas de bash en un solo archivo
- Parece haber sido escrito a mano
- Empezó en 2006 con unos cuantos comandos
openssl - Al analizarlo cae en el issue #606
- rkhunter: programa en Bourne shell de 21K líneas escrito entre 2003 y 2018
- Su sitio oficial es rkhunter.sourceforge.net
- Simplenetes: presentado como “Kubernetes in 17K lines of Shell”
- Se marca como un caso sorprendente, aunque parece estar en estado dormant
- Se enlaza el Hacker News Thread relacionado
- inxi 2.3.56: programa en bash de 16K líneas marcado como obsolete
- Fue un fork de
infobashen 2008 - En ese momento
infobashtenía 889 líneas, yinfobashcomenzó en 2005 - Desde la v2.9,
inxifue reemplazado por una implementación en Perl
- Fue un fork de
- bashdb: depurador de Bash escrito en unas 14K líneas de bash
- Como contexto relacionado se enlaza Implementing Debuggers
- romkatv/powerlevel10k: el directorio
internal/contiene 12K líneas de scripts zsh- Además hay 8K líneas extra de config y helper scripts
- El primer commit es de 2014
- dylanaraps/neofetch: programa de 10K líneas escrito para Bash 3.2 que muestra información del sistema
- También puede hacer funciones interesantes relacionadas con imágenes
- El primer commit es de 2015
- distrobox: script en bash de más de 7K líneas que permite usar cualquier distribución Linux dentro de la terminal
- acme.sh: script de Shell de 8K líneas que emite y renueva certificados
Implementaciones llamativas aunque más pequeñas
- bashforth: con unas 3,800 líneas, no es enorme, pero implementa un lenguaje de programación real
- Tiene muchos espacios en blanco y comentarios
- yoda: mide aproximadamente la mitad que bashforth, pero implementa el intérprete y el compilador completos
- Es una implementación del mismo autor 20 años más joven y con más funciones
- Lleva el comentario “What learned you have, unlearn you must!”
Otros programas de Shell destacados
- abcde / A Better CD Encoder: se usa para ripping de CD y tiene unas 5.5K LoC
- thc-segfault: 3.3K LoC, y en su mayoría es un servidor pubnix hecho en Bash
- ffmpeg/configure: script
configureescrito a mano de FFmpeg con 8.4K LoC - ffhevc: wrapper de Bash totalmente escrito a mano para codificar video HEVC con FFmpeg y libx265, con 4K LoC
- ffx264: wrapper de Bash totalmente escrito a mano para codificar video H.264/AVC con FFmpeg y libx264, con 3.9K LoC
- h264enc: wrapper de Bash totalmente escrito a mano para codificar video H.264/AVC con MEncoder, con 9.2K LoC
- bashtop: monitor de recursos de 5.3K LoC
- halcyon: sistema de instalación de apps Haskell con 6.6K LoC
- Código escrito a mano con atención a la semántica de bash y a la verificación de errores, con un estilo singular inspirado en programación funcional
- wordshell: unas 7K líneas de código para administrar varios sitios de WordPress desde la línea de comandos
- BaCon: unas 10K líneas que convierten programas escritos en BASIC a C
- Tiene tanto implementación en BASIC como implementación en script de Shell
- FireHOL: el script principal tiene 9K líneas, y la herramienta FireQOS añade otras 3K líneas
- Es a la vez un lenguaje y un ejecutor para crear firewalls seguros y con estado a partir de configuraciones legibles para humanos
- gxadmin: 11K LoC de consultas SQL con plantillas y utilidades de procesamiento de datos para administrar el motor de workflows científicos Galaxy
- mulle-bashfunctions: biblioteca de funciones para bash/zsh de unas 6K líneas
- Se usa en
mulle-sde, que a su vez es otro script de Shell de 100K líneas
- Se usa en
- x11docker: 11.6K líneas para ejecutar aplicaciones GUI en contenedores docker o podman
Lenguajes similares a Shell y DSL
- modernish: dialecto portable de shell escrito en Shell
- bats: DSL para escribir pruebas que genera código bash
- bashible: DSL tipo Ansible escrito en bash
- Se enlazan comments relacionados
- clash: framework orientado a objetos compatible con todos los POSIX shell modernos
- bash Infinity: biblioteca estándar y framework boilerplate para bash
Programas más pequeños y ecosistema relacionado
- Los scripts de Alpine, Aboriginal y Debian se enlazan en un blog post aparte
- Los scripts de completion son grandes, pero a menudo repetitivos
- El completion de Zsh para _git tiene 8.3K líneas de código
- También se mencionan
git-completion.bashy Docker completion como ejemplos
- dyne/Tomb: script zsh de unas 3,500 líneas
- Basalt: package manager full-featured escrito en Bash puro
- Tiene unos pocos miles de líneas, pero incluye un rich ecosystem con más de 15 apps y bibliotecas
- bash-core: biblioteca que amplía los builtins
trapyshopty añade stacktrace y funciones esenciales de conveniencia - bash-object: biblioteca en Bash puro para construir estructuras de datos anidadas arbitrarias, con casi 200 pruebas
- bash-json: biblioteca en Bash puro para analizar y emitir JSON
- tablespoon/fun/cli-clock: reloj textual multilínea escrito en bash
- json.bash / jb: herramienta de línea de comandos y biblioteca bash para crear JSON
- Tiene unas 1,700 líneas y unas 3,000 líneas de pruebas
Pruebas de OSH y precauciones sobre el uso de Shell
- OSH "Wild" Tests analizan más de un millón de líneas de Shell
- La mayoría son programas pequeños y definiciones repetitivas de paquetes de distribuciones como Alpine
PKGBUILDy Gentooebuild
- La mayoría son programas pequeños y definiciones repetitivas de paquetes de distribuciones como Alpine
- Shell Programs That Run Under OSH enlaza a una lista de programas de Shell que funcionan bajo OSH
- shell script are dangerous advierte que Shell es un entorno para manipular las entrañas del sistema, ya sea como consola interactiva o de forma no interactiva, con muchas funciones, muy peligroso y no diseñado para crear aplicaciones
1 comentarios
Opiniones de Hacker News
Al investigar, resultó que el OMS era un enorme conjunto de scripts de shell que corría en servidores AIX, había evolucionado durante más de 10 años y luego había quedado abandonado. Tenía más de 50 mil líneas de código; los pedidos, pagos y demás información se movían entre servidores por FTP y luego se parseaban con complejos
sed/awk; incluso el inventario se llevaba en archivos de texto que también se movían por FTP.En ese momento, Perl parecía lo más práctico para trasladar ese desastre, así que empecé por las partes más simples, reemplazándolas por pequeños módulos de Perl y refactorizando de forma gradual dentro de una aplicación Perl más grande. En 3 meses redujimos todo a unas 5 mil líneas de Perl, y el sistema quedó entre 10 y 100 veces más rápido, con casi todas las fallas del sistema original desaparecidas. Fue horrible, pero hasta hoy sigue siendo uno de los trabajos más satisfactorios que he hecho.
Me da curiosidad si leíste todo el código original y lo entendiste a fondo para igualar exactamente su comportamiento, o si descartaste grandes bloques y lo reescribiste según lo que pensabas que “debería hacer”. También me pregunto si había mucho código repetitivo que se podía reemplazar rápido.
El primer script realmente grande que escribí fue un instalador de unas 7 mil líneas para Enrust CA y su directorio, y tenía que correr en prácticamente todos los Unix de la época. No fue así desde el inicio, pero creció por requisitos de clientes.
La instalación en sí no era tan compleja, pero las actualizaciones sí lo eran un poco, y en esa época todas las utilidades variaban ligeramente entre distintos Unix. Una buena parte del script era código para detectar y manejar esas diferencias; también incluía detección de errores, recuperación, rollback y una gestión muy primitiva de paquetes y dependencias.
El Unix de DEC, el que no era Ultrix, fue el más desconcertante. Me tomó varios días darme cuenta de que todas las utilidades de línea de comandos recortaban la salida al ancho de columnas de la terminal, y aún lo recuerdo 30 años después.
HP-UX tenía cambios incompatibles en cada release y, si no recuerdo mal, lo soportábamos desde 6.5 hasta 11. Casi no recuerdo Ultrix, el de Novell, NeXT ni Sequent. Recuerdo que AIX era raro, pero olvidé por qué. Los tres/cuatro sistemas operativos de Sun también tenían diferencias, pero sus manuales eran excelentes y eran lo mejor.
wcsobre el binario principal y las bibliotecas auxiliares de un proyecto, y hasta ahora van 6,224 líneas.Era un script para gestionar pipelines con garantías lineales formados por un adaptador de protocolo de entrada, uno o más filtros y un adaptador de protocolo de salida, todos como un conjunto de contenedores. El objetivo era que pudieran usarlo personas que, sin ser expertas en contenedores ni protocolos, sí sabían cómo querían que los archivos se filtraran y transformaran al pasar por el pipeline.
El binario de nivel superior tiene una estructura con subfunciones, como
git [ git options ] < git action> [action options]osystemctl. Incluso hay un subcomando que agrega nuevos subcomandos: crea las bibliotecas necesarias y rellena de antemano definiciones de funciones a partir de una plantilla. La plantilla incluye funciones de uso corto/largo, de modo quecbap -hocbap pipeline -hden ayuda útil.Hay subcomandos para manipular imágenes base, componentes y pipelines. Buena parte del código es para pruebas que verifican que las definiciones de componentes y pipelines estén escritas correctamente. Los pipelines usan un formato muy parecido a TOML, así que hay código para parsear TOML y convertir secciones en arrays; los componentes son simples archivos
key=value, por lo que hay código para extraer el lado izquierdo y derecho, y validar el esquema.Como los componentes del pipeline pueden compartir atributos, también hay código para buscar atributos comunes en archivos
varyetc, y para especificar atributos de componentes. También hay muchas funciones para manipular usuarios, grupos, directorios y FIFO según requisitos de seguridad. Al configurar un pipeline se crean y aplican usuarios, grupos, tipos de SELinux y categorías MCS, y luego se mapean a archivos de servicio que inician los componentes, así que también hay mucha manipulación de systemd.El grupo más grande de llamadas probablemente sea el de funciones para obtener y configurar atributos de componentes; en realidad, atributos de contenedores. Para cada atributo hay una función de obtención, una de validación y una versión inline dentro del pipeline, para hacer lo más flexibles posible las definiciones de contenedores basadas en datos.
También hay código que usa mucho referencias de Bash para configurar variables desde archivos, variables de entorno y la línea de comandos, lo que permite pruebas rápidas. Soporta cuatro niveles de usuarios: mantenedores que trabajan con el código en sí, desarrolladores que crean definiciones de componentes, integradores que arman pipelines con componentes, y operadores que instalan pipelines; además puede copiarse y empaquetarse a sí mismo para exportarlo a usuarios de cada nivel.
Como el sistema de destino puede ser cualquier Linux, se empaqueta y extrae con
makeself. Por ejemplo, cuando un integrador crea una definición de pipeline, se genera un archivomakeselfy, al ejecutarlo en el sistema de destino, crea todos los usuarios, grupos, directorios y FIFO —es decir, el IPC entre componentes—, aplica DAC/MAC, genera archivos de systemd, copia las imágenes a cada usuario y luego ejecuta el pipeline. Con una opción de eliminación también puede revertir todo eso.También hay algo de seccomp, pero quedó en pausa porque había que encontrar el equilibrio entre listas de permitidos y listas de bloqueados. Uso ShellCheck de forma realmente exhaustiva.
Tampoco podía esperar que todos los entornos tuvieran
bc/dc, y algunas máquinas que tenía usaban versiones antiguas de Bash con soporte muy limitado para arrays asociativos. Como compromiso apunté a AWK, que es un lenguaje de propósito general mucho más agradable que la mayoría de los shells y está en cualquier entorno POSIX: https://beyondloom.com/blog/lila.htmlbc/dcestén en todos los entornos.Me sorprendió bastante que la instalación de Ubuntu en WSL2 no pareciera tener
bc/dc. Para cálculos de punto flotante uso AWK, pero simplemente lo invoco como un proceso externo.Habiendo escrito y mantenido varias veces programas grandes en Perl a lo largo de mi carrera, hay una razón por la que la gente hace esto.
Lenguajes como Java o Python encajan bien cuando las interfaces y los tipos están definidos y casi no hay interacción con el SO. Si usas JSON/XML/YAML, o te comunicas con bases de datos u otros programas por HTTP(S), se da una situación ideal en la que estos lenguajes brillan.
Pero cuando se trata de grandes volúmenes de texto e interacción con el SO, Java y Python se vuelven muy dolorosos. En cambio, Shell/Perl se sienten mucho más cómodos para este tipo de tareas.
Casi todo trabajo de automatización, interfaces confusas y no estandarizadas, archivos de texto/logs, formatos de datos no estructurados o insuficientemente estructurados entran en esta categoría. Si a eso le sumas la compatibilidad hacia atrás de Perl, su amplia base instalada y su rendimiento, prácticamente no hay alternativa real a Perl para estas tareas.
Desde hace mucho vengo pensando que una de las principales razones por las que hoy las grandes empresas emplean a miles de personas para hacer manualmente cosas triviales que podrían automatizarse es que el uso de Perl disminuyó. Intentan hacer grandes trabajos de automatización en Python o Java, pero pronto se rinden, hartos de la verbosidad y del tamaño total del código que hay que escribir y mantener.
Por eso ahora probablemente se necesita más personal caro de tiempo completo. Si los usuarios finales están en Windows, ya tienen en el escritorio una opción parecida a Perl. Es PowerShell, y cumple un rol similar al de Perl.
Bash+grep tiende a derivar en ejecutar un proceso nuevo por cada línea de texto. Para hacerlo de forma eficiente hay que minimizar el trabajo, y para eso se necesita procesamiento por lotes y deduplicación. Eso significa tratar los datos de forma con estado, siguiendo el contexto de deduplicación, algo que es más fácil en un lenguaje de programación de verdad.
Bash+grep favorece el procesamiento de texto sin estado, por lo que es fácil que se acumule trabajo duplicado. Otra forma de reducir el trabajo es filtrar con precisión, y eso es más fácil de expresar limpiamente de manera imperativa en un lenguaje adecuado. grep y las expresiones regulares no sirven en absoluto para este propósito.
Si se usa un formato línea por línea, git agrega escapes para intentar aceptar cualquier cosa, pero el soporte no es consistente y se puede desactivar pidiendo un formato de cadenas terminadas en nulo con la opción
-z. Parece que Bash no tiene forma de manejar esto, mientras que en un lenguaje de nivel suficientemente bajo se trata de forma natural. Además, también es posible hacer streaming incremental sin tener que iniciar un proceso nuevo por cada línea de texto.Como extra, puedes usar una sola base de código para todo, haya HTTP en medio o cualquier otra cosa.
Hay algunos enlaces para dar contexto. “¿Estamos reinventando Perl?”: https://www.oilshell.org/blog/2021/01/why-a-new-shell.html#a...
“El shell de Unix debería evolucionar como Perl 5, con una opción de actualización compatible, no como el Big Bang de Perl 6/Raku”: https://www.oilshell.org/blog/2020/07/blog-roadmap.html#the-...
Recorrido por YSH: https://www.oilshell.org/release/latest/doc/ysh-tour.html
Esto quizá no parezca una gran ventaja hasta que trabajas en un entorno donde no puedes instalar nada desde internet, o directamente no tienes acceso a internet.
El problema central al escribir programas grandes como scripts de Bash es que el lenguaje de shell script no fue diseñado desde el principio para manejar complejidad.
Es excelente para coordinar comandos pequeños y encadenar rápidamente herramientas existentes de forma exploratoria, pero cuando Bash empieza a superar unos cientos de líneas, aparecen una tras otra sus limitaciones, que vuelven difícil el mantenimiento a largo plazo y la escalabilidad.
Primero está la legibilidad. La sintaxis de Bash puede volverse realmente críptica a medida que crece. Las reglas de alcance de variables son sutiles, el manejo de errores es primitivo y la manipulación de strings se ensucia rápido. Al final, quienes mantienen el código pierden tiempo descifrando qué está pasando y les cuesta hacer cambios con confianza.
Luego está la falta de herramientas robustas. Los lenguajes más maduros tienen herramientas de análisis estático, linters y depuradores que ayudan a detectar errores comunes temprano. En Bash, estas cosas no existen o son muy limitadas. Sin esas protecciones, los programas grandes en Bash son más propensos a errores silenciosos, regresiones y bugs sutiles.
Las pruebas también son un problema. Se pueden probar scripts de Bash, pero el proceso suele ser más engorroso, y se vuelve más complicado si hay lógica compleja o estructuras de datos. Al lidiar con casos límite como espacios en nombres de archivo o condiciones inesperadas del entorno, terminas con mucho código defensivo doloroso de verificar.
Por último, el ecosistema en sí no está pensado para desarrollar Bash a gran escala. Pierdes modularidad, gestión de paquetes, manejo estandarizado de dependencias y los patrones de desarrollo modernos que ofrecen Python o Go. Con el tiempo, estas carencias se acumulan y te ralentizan.
Usar Bash para tareas puntuales o automatización simple está bien. Eso es lo que Bash hace bien. Pero si piensas construir algo grande, normalmente conviene usar un lenguaje diseñado para crear y mantener aplicaciones complejas; aunque la curva de aprendizaje inicial o la configuración sean un poco mayores, a largo plazo te ahorrará tiempo.
Usar ShellCheck como linter permite detectar muchas trampas comunes. Bash/shell tiene muchísimas trampas y comportamientos inesperados, al punto de que incluso autores experimentados de Bash pueden caer en ellas
Dicho eso, Bash/shell ocupa una posición particular en la jerarquía de lenguajes. Está prácticamente en todas partes y es muy probable que siga existiendo dentro de 30 años. Si quieres un programa que se ejecute casi en cualquier lugar y que también pueda ejecutarse dentro de 30 años, shell/Bash es una buena opción
Ni siquiera es un script escrito hace mucho tiempo, y no sé por qué eligieron Bash. El script funciona, pero siento que algo se va a romper con solo mirar mal el código
-xcomo argumento de depuración y trazadoProbablemente el programa de shell manual más grande que usaba con regularidad antes era abcde (A Better CD Encoder), de unas 5,500 líneas
https://abcde.einval.com
https://git.einval.com/cgi-bin/gitweb.cgi?p=abcde.git;a=blob...
Muchos de estos programas son verdaderas joyas. Por ejemplo, el script rkhunter tiene código bastante bueno, margen de mejora y además es una mina de información
Gran parte del tamaño del código de estos scripts se dedica a garantizar que existan las utilidades necesarias en varias plataformas y que funcionen como se espera con distintas opciones de línea de comandos. Ese es el punto más doloroso para quien escribe scripts de shell en serio, incluso más que las señales y los subprocesos
Si rkhunter hubiera estado escrito en un lenguaje de programación “de verdad”, creo que esa información habría sido menos transparente. Podría haber quedado relegada a registros dentro de estructuras de datos para luego consultarse, o haber funcionado sobre estructuras de datos anidadas mediante varias funciones o, peor aún, una combinación de métodos y clases. Quizás los logs también habrían quedado fragmentados en JSON y comprimidos en una base de datos, accesibles mediante otros métodos
Como los scripts de shell no tienen esas herramientas complejas, tienden a mostrar de forma más directa lo que está ocurriendo. Por eso rkhunter también sirve como una documentación decente sobre varios exploits y rootkits, y hay menos necesidad de ir excavando de archivo en archivo, de estructura en estructura o de base de datos en base de datos
El cliente de FreeBSD Update tiene unas 3,600 líneas de código sh
No es grande en comparación con otros programas mencionados aquí, pero me parece que la cantidad de funcionalidad —“una herramienta que actualiza todo el sistema operativo”— es bastante considerable. El código que construye las actualizaciones está repartido en varios archivos, así que sumado sería más
poudriereequivale a unos 3 clientes de FreeBSD Update en términos de código sh: https://github.com/freebsd/poudriere/blob/master/src/share/p...Son “apenas” 7.1 mil líneas, pero mi favorito es el script acme.sh, que se usa para emitir y renovar certificados de Let’s Encrypt
https://github.com/acmesh-official/acme.sh/blob/master/acme....
A veces lo único que puedes garantizar que estará disponible es el shell, y hay situaciones en las que la portabilidad es indispensable
Pero, en general, si tienes una aplicación enorme en shell, tal vez deberías reconsiderar tus decisiones de vida
Además, normalmente no se puede hacer mucho solo con el shell; necesitas comandos como
find,grep,sed,cat,head,tailycut. Esos comandos también tienen sus propios problemas de portabilidadApuntar a BusyBox puede ser lo mejor, pero en cuanto sales de un sistema Linux común, escribir scripts portables de Bourne shell se vuelve difícil o casi imposible
Con solo tener un compilador de C, puedes salir del shell y escribir programas en C para que el script de shell los combine, o instalar un mejor lenguaje de scripting como Lua. A estas alturas, los casos en los que necesariamente hay que usar solo shell se sienten bastante de nicho