¿Por qué las utilidades obligatorias POSIX no están integradas en el shell?


45

El propósito de esta pregunta es responder a una curiosidad, no resolver un problema informático en particular. La pregunta es: ¿por qué las utilidades obligatorias POSIX no suelen integrarse en las implementaciones de shell?

Por ejemplo, tengo un script que básicamente lee algunos archivos de texto pequeños y comprueba que estén formateados correctamente, pero tarda 27 segundos en ejecutarse en mi máquina, debido a una cantidad significativa de manipulación de cadenas. Esta manipulación de cadenas crea miles de nuevos procesos al llamar a varias utilidades, de ahí la lentitud. Estoy bastante seguro de que si algunos de los servicios públicos fueron construidos en, a saber grep, sed, cut, tr, y expr, a continuación, la secuencia de comandos se ejecutaría en un segundo o menos (basado en mi experiencia en C).

Parece que habría muchas situaciones en las que construir estas utilidades marcaría la diferencia entre si una solución en el script de shell tiene un rendimiento aceptable.

Obviamente, hay una razón por la que se eligió no hacer estas utilidades integradas. Quizás tener una versión de una utilidad a nivel de sistema evita que varias shells utilicen varias versiones desiguales de esa utilidad. Realmente no puedo pensar en muchas otras razones para mantener la sobrecarga de crear tantos procesos nuevos, y POSIX define lo suficiente sobre las utilidades para que no parezca un gran problema tener diferentes implementaciones, siempre que sean POSIX obediente. Al menos no es un problema tan grande como la ineficiencia de tener tantos procesos.


15
Si 27 segundos es demasiado lento, puede usar Python, Perl o algún otro lenguaje semi-compilado. Alternativamente, publique las partes lentas de su script y solicite mejoras. Puede ser que esté utilizando tres o cuatro comandos donde podría hacerlo uno (el más rápido).
roaima

8
Desafortunadamente, los shells no están hechos para tareas pesadas, y el mundo ha cambiado mucho desde los tiempos en que se podía escapar con solo un script de shell. Estoy de acuerdo con Roaima: todos los administradores de sistemas razonables deberían elegir Python o Perl y no esperar que el shell se encargue de todo
Sergiy Kolodyazhnyy

16
El propósito principal del shell es ejecutar otros programas, no manipular datos directamente. A lo largo de los años, algunos programas externos o funciones que proporcionan (globbing, aritmética printf, etc.) se han incorporado a los shells cuando se consideraron lo suficientemente útiles.
chepner

8
Si publica su script en codereview.stackexchange.com, estoy seguro de que los revisores podrían hacer algunas sugerencias para acelerar su script de manera drástica (o al menos señalar por qué debería escribirse en Python / etc en lugar de shell).
chepner

55
@ Kyle: awkes una utilidad obligatoria en POSIX, y especialmente adecuado (es decir, muy rápido) para implementar scripts que de otro modo podrían implementar usando sed, cut, tr, grep, y expren un script de shell.
Nominal Animal

Respuestas:


11

No se espera que los scripts de shell se ejecuten con ese tipo de velocidad. Si quieres mejorar la velocidad de tu script, pruébalo en perl. Si todavía es demasiado lento, tendrá que pasar a un lenguaje estáticamente escrito como java o c, o escribir un módulo C para perl que ejecute las partes que son demasiado lentas.

Shell es el primer nivel de creación de prototipos, si puede probar el concepto con shell, entonces pase a un mejor lenguaje de scripting que pueda verificar más límites, lo que requeriría acres de shell.

Se espera que un sistema operativo Unix incluya muchos programas pequeños que realizan tareas bien definidas que conforman una imagen más grande. Esto es bueno, ya que compartimenta programas más grandes. Eche un vistazo a qmail, por ejemplo, y compárelo con sendmail. qmail está hecho de muchos programas:

http://www.nrg4u.com/qmail/the-big-qmail-picture-103-p1.gif

Explotar el demonio de red no lo ayudaría a explotar el gestor de colas.


El OP específicamente NO solicitó sugerencias para mejorar la velocidad del código. La pregunta era por qué ciertas utilidades no son incorporadas como cdo pwd.
Stephen C

44
Cierto. La respuesta fue expresar la diferencia entre monolítico y compartimentado y mostrar una razón a este favor.
Ed Neville


1
@StephenC cdestá integrado , y en realidad tiene que serlo, porque cambiar el directorio de trabajo en un subproceso no afecta los procesos principales.
Jonas

67

¿Por qué las utilidades obligatorias POSIX no están integradas en shell?

Debido a que es compatible con POSIX, se requiere un sistema 1 para proporcionar la mayoría de las utilidades como comandos independientes.

Tenerlos incorporados implicaría que tienen que existir en dos lugares diferentes, dentro del caparazón y fuera de él. Por supuesto, sería posible implementar la versión externa mediante el uso de un contenedor de script de shell en el builtin incorporado, pero eso perjudicaría a las aplicaciones no shell que llaman a las utilidades.

Tenga en cuenta que BusyBox tomó el camino que sugirió al implementar muchos comandos internamente y proporcionar la variante independiente mediante enlaces a sí mismo. Un problema es que si bien el conjunto de comandos puede ser bastante grande, las implementaciones a menudo son un subconjunto del estándar, por lo que no son compatibles.

Tenga en cuenta también que, al menos ksh93, bashy zshvaya más allá al proporcionar métodos personalizados para que el shell en ejecución cargue dinámicamente los builtins desde bibliotecas compartidas. Técnicamente, nada impide que todas las utilidades POSIX se implementen y estén disponibles como incorporadas.

Finalmente, generar nuevos procesos se ha convertido en una operación bastante rápida con sistemas operativos modernos. Si realmente se ve afectado por un problema de rendimiento, puede haber algunas mejoras para que sus scripts se ejecuten más rápido.

1 POSIX.1-2008

Sin embargo, todas las utilidades estándar , incluidas las incorporaciones regulares en la tabla, pero no las incorporaciones especiales descritas en Utilidades incorporadas especiales, se implementarán de manera que se pueda acceder a ellas a través de la familia ejecutiva de funciona como se define en el volumen de Interfaces del sistema de POSIX.1-2008 y puede ser invocado directamente por las utilidades estándar que lo requieren (env, find, nice, nohup, time, xargs).


44
Esta es la respuesta correcta, pero solo agregaría que, como la interfaz de estas utilidades generalmente es a través de stdin / stdout de todos modos, incluso si cada una de ellas también se implementara como una rutina incorporada en bash, aún sería necesario para bifurcarse y crear tuberías para cada comando en una tubería de todos modos, por lo que solo habría ganancias marginales
Chunko

2
@Chunko Sí. Sin embargo, las subcapas son más ligeras que los procesos fork / exec'ed.
jlliagre

3
@Slebetman Te estás perdiendo mi punto. Las subcapas no son subprocesos ni procesos ejecutados, independientemente de si se ejecutan en Linux o no. Las subcapas son solo el clon de sus padres, creado por un fork no seguido por exec; forkHoy en día es una operación muy ligera en comparación con exec.
jlliagre

3
Medí busybox noforkórdenes internas que tienen del orden de 10 veces menos sobrecarga que noexecbuiltins, que a su vez tenía ~ 5x menos sobrecarga que tenedor + exec de un binario independiente. Definiciones según unix.stackexchange.com/a/274322/29483 Es interesante que busybox no lo sea noforktodo, aunque sé que algunos códigos de busybox se acortan al no limpiar la memoria, y solo se basan en un proceso de corta duración.
sourcejedi

1
@jlliagre: en Linux, una bifurcación crea un proceso. El punto que tal vez se esté perdiendo es que en Linux han optimizado tanto los procesos que los desarrolladores han determinado que no hay más ventaja al crear algo más liviano. Básicamente, en Linux, un proceso es tan ligero como un hilo.
slebetman

9

Del manual de referencia de BASH ,

Los comandos incorporados son necesarios para implementar funcionalidades imposibles o inconvenientes de obtener con utilidades separadas.

Como estoy seguro de que has escuchado, la filosofía de UNIX se basa en gran medida en múltiples aplicaciones que tienen una funcionalidad limitada. Cada incorporado tiene una muy buena razón por la que está incorporado. Todo lo demás no lo está. Creo que una clase de preguntas más interesante es, "¿por qué exactamente está pwd incorporado?"


2
En una palabra: Modularidad
Peschke

2
/ bin / pwd existe. Creo que cdsería un mejor ejemplo aquí de algo que es imposible de implementar como una herramienta separada.
Oskar Skog

1
@OskarSkog Ese era el punto. cdtiene que ser incorporado, pwdno. Entonces, ¿por qué los bashimplementadores decidieron incluirlo?
Stig Hemmer

1
... que está cubierto por unix.stackexchange.com/questions/145479 .
JdeBP

@StigHemmer /bin/bashexiste, pero sigue siendo una construcción. Vea la lista de incorporados en gnu.org/software/bash/manual/html_node/…
Stephen C

8

Los muchachos de AT&T se preguntaron lo mismo

Si nos fijamos en la historia del AT&T Software Toolkit (actualmente latente en Github desde que el equipo central se fue), esto es exactamente lo que hicieron con el shell AT&T Korn, también conocido como ksh93.

El rendimiento siempre fue parte de la motivación para los mantenedores de ksh93, y al compilar ksh puede elegir construir muchas utilidades POSIX comunes como bibliotecas cargadas dinámicamente. Al vincular estos comandos a un nombre de directorio como /opt/ast/bin, puede controlar qué versión del comando se usará, en función de la posición de ese nombre de directorio $PATH.

Ejemplos:

cat chmod chown cksum cmp cp cut date expr fmt head join ln
mkdir mkfifo mktemp mv nl od paste rm tail tr uniq uuencode wc

La lista completa se puede encontrar en el repositorio github ast .

Tenga en cuenta que la mayoría de las herramientas de AST tienen su propia procedencia y diferirían fuertemente de las implementaciones de GNU más comunes. El equipo de investigación de AT&T cumplió con los estándares oficiales, que era la forma de lograr la interoperabilidad cuando no se podía compartir el código.


6

Por lo tanto, no reunimos recursos para optimizar la herramienta original, para satisfacer cada deseo específico. Creo que lo que necesitamos explicar es cuánto habría costado implementar este deseo específico.

POSIX define lo suficiente sobre las utilidades como para que no parezca un gran problema tener diferentes implementaciones.

Esta es una mala suposición :-P.

Los sistemas post-POSIX continúan siendo más potentes y convenientes por buenas razones; como estándar después de los hechos, nunca se pone al día.

Ubuntu comenzó un esfuerzo para cambiar a un shell POSIX simplificado para secuencias de comandos, para optimizar el antiguo proceso de inicio de System V init. No digo que haya fallado, pero desencadenó muchos errores que tuvieron que limpiarse: "bashisms", scripts que se ejecutaron /bin/shmientras se suponía que las bashfunciones estaban disponibles.

POSIX sh no es un buen lenguaje de programación de propósito general. Su propósito principal es funcionar bien como un shell interactivo. Tan pronto como comience a guardar sus comandos en un script, tenga en cuenta que se acerca a una tarpit de Turing . Por ejemplo, no es posible detectar fallas en medio de una tubería normal . bashagregado set -o pipefailpara esto, pero esto no está en POSIX.

Casi todas las utilidades más complejas que proporcionan funciones útiles pero no estandarizadas similares true.

Para la clase de tarea que delinees, puedes dibujar una línea aproximada para Awk, Perl y hoy en día Python. Se crearon diferentes herramientas y evolucionaron de forma independiente. ¿Esperaría, por ejemplo, que GNU Awk se incluyera en un libutilposixextended?

No estoy diciendo que ahora tengamos un enfoque universalmente mejor al que pueda señalarle. Tengo una debilidad por Python. Awk es sorprendentemente poderoso, aunque algunas características de GNU Awk me han frustrado. Pero el punto es que procesar grandes cantidades de cadenas individualmente (presumiblemente de las líneas de los archivos) no era un objetivo de diseño del shell POSIX.


Me pregunto si habría alguna dificultad con un shell que supondría que cualquier comando ejecutado desde una lista configurable de ubicaciones sería tratado como incorporado en los casos en que el shell entendiera todo sobre el comando. Si un script ejecuta cat -@fnord fooel shell, debería decidir eso, ya que no sabe qué -@significa que necesitaría invocar el comando real, pero dado que cat <foo >barel shell no debería generar otro proceso.
supercat

1
@supercat complejidad.
sourcejedi

2

También está la cuestión de: ¿En qué shell lo construirías?

La mayoría de los sistemas Unix / Linux tienen múltiples shells diferentes que se desarrollan de forma independiente (sh / bash / korn / ???). Si construye las herramientas en el shell, terminaría con una implementación diferente de estas herramientas para cada shell. Esto provocaría una sobrecarga, y podría terminar con diferentes características / errores en, por ejemplo, grep, dependiendo de qué shell utilizó para invocarlo.


zsh es bastante popular en algunos círculos en estos días. Históricamente, csh / tcsh ha tenido un gran seguimiento, pero no creo que veas mucho hoy. Y hay un paquete completo de proyectiles menos conocidos ...
un CVn

Modularidad. Con los builtins, necesitará recompilar o reinstalar el shell cada vez que se realice un cambio en uno de esos builtins.
can-ned_food

1

Muchos han respondido bien. Solo pretendo complementar esas respuestas. Creo que la filosofía de UNIX es que una herramienta debe hacer una cosa y hacerlo bien. Si uno trata de hacer una herramienta que lo abarque todo, hay muchos más lugares para el fracaso. Limitar la funcionalidad de esta manera hace que un conjunto de herramientas sea confiable.

Además, tenga en cuenta que si se integraran funciones como sed o grep en el shell, ¿sería tan fácil invocar desde la línea de comandos cuando lo desee?

Para terminar, considere que algunas de las funcionalidades que desea tener en BASH están en BASH . Por ejemplo, la capacidad para la coincidencia de RE en BASH se implementa utilizando el operador binario = ~ (consulte Gramática de Shell en la página del manual para obtener más información específica sobre la discusión de la construcción [[]] para if ). Como un ejemplo muy rápido, digamos que estoy buscando un archivo de 2 dígitos hexadecimales:

while read line; do
    if [[ $line =~ 0x[[:xdigit:]]{2} ]]; then
        # do something important with it
    fi
done < input_file.txt

En cuanto a la funcionalidad de tipo sed , busque en Expansión de parámetros en el encabezado Expansión de la misma página de manual. Verás una gran cantidad de cosas que puedes hacer que recuerdan a sed. La mayoría de las veces uso sed para hacer algún cambio de tipo de sustitución en el texto. A partir de lo anterior:

# this does not take into account the saving of the substituted text
# it shows only how to do it
while read line; do
    ${line/pattern/substitution}
done < input_file.txt

Al final, ¿es lo anterior "mejor" que?

grep -E "[[:xdigit:]]{3}" input_file.txt
sed -e 's/pattern/substitution/' input_file.txt

Puede encontrar un argumento en contra de la última pregunta en unix.stackexchange.com/questions/169716/…
phk

1

Esto es, supongo, un accidente histórico.

Cuando se creó UNIX a fines de los años sesenta y principios de los setenta, las computadoras no tenían casi tanta memoria como hoy en día. Hubiera sido posible, en ese momento, implementar toda esta funcionalidad como componentes integrados de shell, pero debido a las limitaciones de memoria, habrían tenido que limitar la cantidad de funcionalidad que podrían implementar, o arriesgarse de memoria y / o intercambiar basura problemas.

Por otro lado, al implementar la funcionalidad dada como programas separados, y al hacer las dos llamadas al sistema requeridas para comenzar un nuevo proceso lo más ligero posible, podrían crear un entorno de secuencias de comandos que no tenga esos problemas y que todavía se ejecute a un nivel razonable velocidad.

Por supuesto, una vez que esas cosas se implementen como procesos separados, las personas los iniciarán desde programas que no son shells, y luego tendrán que permanecer así, o de repente todo este software comenzará a romperse.

Sin embargo, eso no quiere decir que no pueda implementar alguna funcionalidad dos veces, y de hecho algunos shells implementan alguna funcionalidad que se supone que es un programa externo como un shell incorporado; por ejemplo, bash implementa el echocomando como incorporado, pero también hay un/usr/bin/echo

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.