Pourquoi les utilitaires POSIX obligatoires ne sont-ils pas intégrés au shell?

45

Le but de cette question est de répondre à une curiosité et non de résoudre un problème informatique particulier. La question qui se pose est la suivante: pourquoi les utilitaires POSIX obligatoires ne sont-ils généralement pas intégrés aux implémentations de shell?

Par exemple, j’ai un script qui lit en principe quelques petits fichiers texte et vérifie qu’ils sont correctement formatés, mais il faut 27 secondes pour s’exécuter, sur ma machine, en raison d’un nombre considérable de manipulations de chaînes. Cette manipulation de chaîne crée des milliers de nouveaux processus en appelant divers utilitaires, d’où la lenteur. Je suis assez confiant que si certains des services publics ont été intégrés, à savoir grep, sed, cut, tret expr, le script courrait dans une seconde ou moins ( d' après mon expérience en C).

Il semble que dans de nombreux cas, la construction de ces utilitaires puisse faire la différence entre une solution dans un script shell et des performances acceptables.

De toute évidence, il y a une raison pour laquelle il a été choisi de ne pas intégrer ces utilitaires. Peut-être qu'avoir une version d'un utilitaire au niveau du système évite que plusieurs versions inégales de cet utilitaire soient utilisées par différents shells. Je ne peux vraiment pas penser à de nombreuses autres raisons de garder la surcharge de créer autant de nouveaux processus, et POSIX définit suffisamment les utilitaires pour qu'il ne semble pas être un problème d'avoir différentes implémentations, tant qu'ils sont tous POSIX conforme. Du moins pas un problème aussi important que l'inefficacité d'avoir autant de processus.

Kyle
la source
15
Si 27 secondes est trop lent, vous pouvez utiliser Python, Perl ou un autre langage semi-compilé. Vous pouvez également poster les parties lentes de votre script et demander des améliorations. Il se peut que vous utilisiez trois ou quatre commandes là où une (plus rapide) pourrait faire.
Roaima
8
Les obus n'étaient pas vraiment conçus pour des tâches lourdes. Malheureusement, le monde a beaucoup changé depuis l'époque où l'on pouvait se contenter d'un script shell. Je suis d'accord avec roaima - chaque administrateur système raisonnable devrait aller pour Python ou Perl et ne pas s'attendre à ce que le shell gère tout
Sergiy Kolodyazhnyy 23/02/17
16
L'objectif principal du shell est d'exécuter d'autres programmes et non de manipuler des données directement. Au fil des ans, certains programmes ou fonctionnalités externes fournis par ceux-ci (globbing, arithmétique printf, etc.) ont été incorporés dans des coquilles lorsqu'elles ont été jugées suffisamment utiles.
Chepner
8
Si vous postez votre script sur codereview.stackexchange.com, je suis certain que les relecteurs pourront faire quelques suggestions pour accélérer considérablement votre script (ou au moins indiquer pourquoi il devrait être écrit en Python / etc au lieu de shell).
Chepner
5
@Kyle: awkest un utilitaire obligatoire POSIX, et convient particulièrement bien (qui est, très rapide) pour mettre en œuvre des scripts que vous pourriez mettre en œuvre en utilisant autrement sed, cut, tr, grepet exprdans un script shell.
Nominal Animal

Réponses:

11

Les scripts shell ne doivent pas s'exécuter avec ce type de vitesse. Si vous voulez améliorer la vitesse de votre script, essayez-le en Perl. Si cela est toujours trop lent, vous devrez alors passer à un langage statiquement typé tel que java ou c, ou écrire un module C pour perl qui exécute les parties trop lentes.

Shell est le premier niveau de prototypage, si vous pouvez prouver le concept avec shell, puis passez à un meilleur langage de script qui peut faire plus de vérifications en vérifiant que cela prendrait des acres de shell.

Un système d'exploitation Unix devrait inclure de nombreux petits programmes qui effectuent des tâches bien définies qui constituent une image plus grande. C’est une bonne chose car cela compartimente les plus gros programmes. Jetez un coup d'œil à qmail, par exemple, et comparez-le à sendmail. qmail est composé de nombreux programmes:

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

L'exploitation du démon réseau ne vous aiderait pas à exploiter le gestionnaire de files d'attente.

Ed Neville
la source
Le PO n'a en particulier PAS demandé de suggestions pour améliorer la vitesse du code. La question était de savoir pourquoi certains utilitaires ne sont pas intégrés, comme cdou pwd.
Stephen C
4
Vrai. La réponse a été d'exprimer la différence entre monolithique et compartimenté et de montrer une raison en ce sens.
Ed Neville
1
@StephenC cdest intégré - et il doit l'être, car la modification du répertoire de travail dans un sous-processus n'affecte pas les processus parents.
Jonas
67

Pourquoi les utilitaires obligatoires POSIX ne sont-ils pas intégrés au shell?

Pour être conforme à POSIX, un système 1 est requis pour fournir la plupart des utilitaires sous forme de commandes autonomes.

Leur intégration impliquerait qu'ils doivent exister dans deux endroits différents, à l'intérieur et à l'extérieur du shell. Bien sûr, il serait possible d'implémenter la version externe en utilisant un encapsuleur de script shell à la commande intégrée, mais cela désavantagerait les applications non-shell appelant les utilitaires.

Notez que BusyBox a emprunté le chemin que vous avez suggéré en implémentant de nombreuses commandes en interne et en fournissant la variante autonome en utilisant des liens vers elle-même. L'un des problèmes est que, si le jeu de commandes peut être assez volumineux, les implémentations sont souvent un sous-ensemble de la norme et ne sont donc pas conformes.

Notez également qu’au moins ksh93, bashet zshallez plus loin en fournissant des méthodes personnalisées pour que le shell en cours d’exécution charge dynamiquement les commandes intégrées à partir de bibliothèques partagées. Techniquement, rien n'empêche alors tous les utilitaires POSIX d'être implémentés et rendus disponibles en tant que fonctions intégrées.

Enfin, la création de nouveaux processus est devenue une opération assez rapide avec les systèmes d’exploitation modernes. Si vous rencontrez un problème de performances, des améliorations pourraient être apportées pour accélérer l'exécution de vos scripts.

1 POSIX.1-2008

Cependant, tous les utilitaires standard , y compris les éléments intégrés classiques du tableau, mais pas les éléments intégrés spéciaux décrits dans Utilitaires intégrés spéciaux, doivent être implémentés de manière à pouvoir être accessibles via la famille exec de fonctions définies dans le volume Interfaces système de POSIX.1-2008 et pouvant être invoquées directement par les utilitaires standard qui en ont besoin (env, recherche, sympa, nohup, heure, xargs).

jlliagre
la source
4
C’est la bonne réponse, mais j’aimerais juste ajouter que comme l’interface de ces utilitaires est généralement via stdin / stdout de toute façon, même si chacun d’entre eux était également implémenté comme une routine intégrée à bash, il aurait effectivement encore besoin de se fourrer et créer des tuyaux pour chaque commande dans un pipeline de toute façon, de sorte qu'il n'y aurait que des gains marginaux
Chunko
2
@Chunko Oui. Les sous-shell sont cependant plus légers que les processus fork / exec.
jeudi
3
@slebetman Vous manquez mon point. Les sous-shells ne sont ni des threads ni des processus exécutés, qu'ils fonctionnent ou non sous Linux. Les sous-shell ne sont que le clone de leur parent, créé par un fork non suivi de exec; forkest de nos jours une opération très légère comparée à exec.
juillet
3
J'ai mesuré les commandes noforkintégrées de busybox comme ayant environ 10 fois moins de temps système que les commandes noexecintégrées, qui à leur tour avaient environ 5 fois moins de temps système que fork + exec d'un fichier binaire séparé. Définitions selon unix.stackexchange.com/a/274322/29483 Il est intéressant de noter que busybox ne fait pas noforktout, bien que je sache que le code de busybox est raccourci en ne nettoyant pas la mémoire et qu'il repose simplement sur un processus éphémère.
sourcejedi
1
@jlliagre: Sous Linux, un fork crée un processus. Ce qui vous manque peut-être, c’est que sous Linux, ils ont tellement optimisé les processus que les développeurs ont déterminé qu’il n’y avait plus d’avantage à créer quelque chose de plus léger. En gros, sous Linux, un processus est aussi léger qu'un thread.
Slebetman
9

À partir du manuel de référence BASH ,

Les commandes intégrées sont nécessaires pour implémenter des fonctionnalités impossibles ou peu pratiques à obtenir avec des utilitaires distincts.

Comme vous le savez sans doute, la philosophie UNIX repose sur de nombreuses applications aux fonctionnalités limitées. Chaque élément intégré a une très bonne raison pour laquelle il est intégré. Tout le reste ne l'est pas. Je pense qu'une catégorie de questions plus intéressante va dans le sens de, "pourquoi est pwd -ce exactement intégré?"

Stephen C
la source
2
En un mot: Modularité
Peschke
2
/ bin / pwd existe. Je pense que ce cdserait un meilleur exemple de ce qui est impossible à mettre en œuvre séparément.
Oskar Skog
1
@OskarSkog C'était le but. cddoit être intégré, pwdne le fait pas. Alors pourquoi les bashdéveloppeurs ont-ils choisi de l'inclure?
Stig Hemmer le
1
... qui est recouvert par unix.stackexchange.com/questions/145479 .
JdeBP
@StigHemmer /bin/bashexiste, mais c'est toujours intégré. Voir la liste des éléments intégrés sur gnu.org/software/bash/manual/html_node/…
Stephen C
8

Les gars d'AT & T se sont demandé la même chose

Si vous examinez l'historique de la boîte à outils AT & T Software (actuellement en attente sur github depuis le départ de l'équipe principale), c'est exactement ce qu'ils ont fait avec le shell AT & T Korn, alias ksh93.

Les performances ont toujours été une partie de la motivation des responsables de ksh93, et lors de la construction de ksh, vous pouvez choisir de construire de nombreux utilitaires POSIX courants sous forme de bibliothèques à chargement dynamique. En liant ces commandes à un nom de répertoire comme /opt/ast/bin, vous pouvez contrôler la version de la commande à utiliser, en fonction de la position de ce nom de répertoire dans $PATH.

Exemples:

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 liste complète se trouve dans le répertoire github ast .

Notez que la plupart des outils ast ont leur propre provenance et différeraient fortement des implémentations plus courantes de gnu. L'équipe de recherche d'AT & T s'est conformée aux normes officielles, ce qui était le moyen de parvenir à l'interopérabilité lorsqu'il était impossible de partager le code.

Henk Langeveld
la source
6

Nous n'avons donc pas mobilisé les ressources pour optimiser l'outil d'origine, afin de répondre à tous les souhaits. Je suppose que ce qu’il faut expliquer, c’est combien ce désir spécifique aurait un coût de mise en œuvre.

POSIX définit suffisamment les utilitaires pour qu'il ne semble pas y avoir de problème avec différentes implémentations.

c'est une mauvaise hypothèse :-P.

Les systèmes post-POSIX continuent de devenir plus puissants et plus pratiques pour de bonnes raisons; En tant que norme après coup, elle ne rattrape jamais réellement.

Ubuntu a commencé à essayer de passer à un shell POSIX dépouillé pour les scripts, afin d'optimiser l'ancien processus d'initialisation de System V. Je ne dis pas qu'il a échoué, mais il a fait déclencher de nombreux bugs qui ont dû être nettoyé: « bashismes », les scripts qui ont paru sous /bin/shtout en supposant que les bashcaractéristiques étaient disponibles.

POSIX sh n’est pas un bon langage de programmation généraliste. Son objectif principal est de fonctionner correctement en tant que shell interactif. Dès que vous commencez à enregistrer vos commandes dans un script, sachez que vous vous approchez d'un tarpit Turing . Par exemple, il est impossible de détecter des défaillances au milieu d'un pipeline normal . bashajouté set -o pipefailpour cela, mais ce n'est pas dans POSIX.

Des fonctionnalités similaires, utiles mais non standardisées, sont fournies par presque tous les utilitaires plus complexes que true.

Pour la classe de tâches que vous décrivez, vous pouvez tracer une ligne approximative entre Awk, Perl et, de nos jours, Python. Différents outils ont été créés et ont évolué indépendamment. Vous attendriez-vous par exemple à ce que GNU Awk soit intégré à un libutilposixextended?

Je ne dis pas que nous avons maintenant une approche universellement meilleure que je peux vous indiquer. J'ai un faible pour Python. Awk est étonnamment puissant, même si certaines fonctionnalités spécifiques à GNU Awk m'ont frustré. Mais le fait est que le traitement d'un grand nombre de chaînes individuellement (probablement à partir de lignes de fichiers) n'était pas un objectif de conception du shell POSIX.

sourcejedi
la source
Je me demande si un shell n’aurait aucune difficulté à supposer que toute commande exécutée à partir d’une liste configurable d’emplacements serait traitée comme une commande intégrée dans les cas où le shell comprendrait tout sur la commande? Si un script exécute, cat -@fnord foole shell devrait décider de ne pas définir le -@moyen nécessaire pour appeler la commande réelle, mais cat <foo >barle shell ne devrait pas avoir besoin de générer un autre processus.
Supercat
1
Complexité @ supercat.
sourcejedi
2

Il y a aussi la question de: dans quel shell voulez-vous le construire?

La plupart des systèmes Unix / Linux ont plusieurs shells différents qui sont développés indépendamment (sh / bash / korn / ???). Si vous construisez les outils dans le shell, vous obtiendrez une implémentation différente de ces outils pour chaque shell. Cela entraînerait une surcharge, et vous pourriez vous retrouver avec différentes fonctionnalités / bogues dans, par exemple, grep, selon le shell que vous utilisiez pour l'appeler.

Tandis que
la source
zsh est très populaire dans certains milieux ces jours-ci. csh / tcsh a toujours attiré un grand nombre de personnes, mais je ne pense pas que vous en voyiez beaucoup aujourd'hui. Et il y a tout un paquet d'obus moins connus ...
un CVn
Modularité. Avec les commandes intégrées, vous devez recompiler ou réinstaller le shell chaque fois qu'une modification est apportée à l'une de ces commandes intégrées.
can-ned_food
1

Beaucoup ont bien répondu. Je compte seulement complimenter ces réponses. Je pense que la philosophie UNIX est qu’un outil doit faire une chose et le faire bien. Si l’on essaie de créer un outil complet, c’est beaucoup plus d’échecs. Limiter la fonctionnalité de cette manière rend un jeu d’outils fiable.

En outre, considérez que si des fonctionnalités telles que sed ou grep étaient intégrées au shell, serait-il aussi facile d'appeler à partir de la ligne de commande quand vous le souhaiteriez?

En terminant, considérons que certaines des fonctionnalités que vous souhaitez intégrer à BASH se trouvent dans BASH . Par exemple, la possibilité de correspondance RE dans BASH est implémentée à l'aide de l' opérateur binaire = ~ (voir Shell Grammar dans la page Manual pour plus d'informations, reportez-vous à la discussion de la construction [[]] pour if ). Comme exemple très rapide, disons que je cherche un fichier avec 2 chiffres hexadécimaux:

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

En ce qui concerne les fonctionnalités de type sed , consultez la rubrique Extension de paramètre dans l' en -tête Expansion de la même page de manuel. Vous verrez une multitude de choses que vous pouvez faire qui rappellent sed. J'utilise le plus souvent sed pour effectuer un changement de type de substitution en texte. S'appuyant sur ce qui précède:

# 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

En fin de compte, est-ce que ce qui précède est "meilleur" que?

grep -E "[[:xdigit:]]{3}" input_file.txt
sed -e 's/pattern/substitution/' input_file.txt
Andrew Falanga
la source
Un argument contre la dernière question peut être trouvé sous unix.stackexchange.com/questions/169716/…
phk
1

C'est, je suppose, un accident historique.

Lorsque UNIX a été créé à la fin des années 1960 et au début des années 1970, les ordinateurs n’avaient pas autant de mémoire qu’aujourd’hui. À l'époque, il aurait été possible d'implémenter toutes ces fonctionnalités en tant que commandes intégrées au shell, mais en raison de limitations de mémoire, il leur aurait fallu limiter la quantité de fonctionnalités qu'elles pourraient implémenter, ou risquer de manquer de mémoire et / ou de supprimer la corbeille d'échange. problèmes.

D'autre part, en implémentant la fonctionnalité donnée en tant que programmes distincts et en rendant les deux appels système requis pour démarrer un nouveau processus aussi léger que possible, ils pourraient créer un environnement de script qui ne présente pas ces problèmes et qui fonctionne toujours à un rythme raisonnable. la vitesse.

Bien sûr, une fois que ces éléments sont mis en œuvre en tant que processus distincts, les utilisateurs les lancent à partir de programmes qui ne sont pas des shells. Ils doivent ensuite rester comme ça ou tout à coup, tout ce logiciel commence à casser.

Cela ne veut pas dire que vous ne pouvez pas implémenter certaines fonctionnalités à deux reprises, et en fait, certains shells implémentent des fonctionnalités qui sont supposées être un programme externe intégré à un shell; par exemple, bash implémente la echocommande en tant que fonction intégrée, mais il y a aussi un/usr/bin/echo

Wouter Verhelst
la source