Quelle est la stabilité des «API stdin / stdout» du shell Unix?

20

grepping, awking, sedding et piping sont la routine quotidienne d'un utilisateur de n'importe quel système d'exploitation de type Unix, que ce soit sur la ligne de commande ou à l'intérieur d'un script shell (collectivement appelés filtres désormais).

Dans leur essence, lorsqu'ils fonctionnent avec des programmes CLI Unix "standard" et des commandes internes (appelées désormais commandes collectivement ), les filtres ont besoin d'un format attendu précis pour stdin, stdout et stderr à chaque étape du filtre afin de fonctionner correctement. J'appelle ce format précis attendu d'une commande une API de cette commande dans ce qui suit.

En tant qu'expert en développement Web, je compare ce type de collecte et de traitement de données techniquement avec le raclage Web - une technique qui est très instable chaque fois qu'il y a le moindre changement dans la présentation des données.

Ma question concerne maintenant la stabilité des API de commande Unix.

  1. Les commandes d'un système d'exploitation de type Unix adhèrent-elles à une normalisation formelle en ce qui concerne leur entrée et sortie?
  2. Y a-t-il eu des cas dans l'histoire où des mises à jour d'une commande importante ont causé la rupture de la fonctionnalité d'un filtre créé à l'aide d'une ancienne version de ladite commande?
  3. Les commandes Unix ont-elles évolué au fil du temps qu'il est absolument impossible de modifier de telle manière qu'un filtre pourrait se casser?
  4. Dans le cas où les filtres peuvent se casser de temps en temps en raison de la modification des API de commande, comment puis-je en tant que développeur protéger mes filtres contre ce problème?
Abdull
la source

Réponses:

17

La norme POSIX 2008 comporte une section décrivant «Shell et utilitaires» . Généralement, si vous vous en tenez à cela, vos scripts devraient être assez à l'épreuve du temps, sauf éventuellement pour les dépréciations, mais ceux-ci ne se produisent pratiquement pas du jour au lendemain, vous devriez donc avoir beaucoup de temps pour mettre à jour vos scripts.

Dans certains cas où le format de sortie d'un utilitaire unique varie considérablement d'une plate-forme et d'une version à l'autre, la norme POSIX peut inclure une option généralement appelée -pou -Pqui spécifie un format de sortie garanti et prévisible. Un exemple de ceci est l' timeutilitaire , qui a des implémentations très différentes. Si vous avez besoin d'un format API / sortie stable, vous utiliseriez time -p.

Si vous devez utiliser un utilitaire de filtrage qui n'est pas couvert par la norme POSIX, vous êtes à peu près à la merci des conditionneurs de distribution / développeurs en amont, tout comme vous êtes à la merci des développeurs Web distants lors du scraping Web.

jw013
la source
12

Je vais essayer de répondre de mon expérience.

  1. Les commandes n'adhèrent pas vraiment à une spécification formelle, mais elles respectent l'exigence de consommer et de générer du texte orienté ligne.

  2. Oui bien sûr. Avant que les utilitaires GNU ne deviennent une norme de facto, de nombreux fournisseurs auraient une sortie originale, en particulier en ce qui concerne pset ls. Cela a causé beaucoup de douleur. Aujourd'hui, seul HP propose des commandes super originales. Historiquement, les utilitaires Berkeley Software Distribution (BSD) ont été une rupture majeure avec le passé. La spécification POSIX était une rupture avec le passé, mais maintenant elle est largement acceptée.

  3. Les commandes Unix ont en effet évolué avec le temps. Il n'est toujours pas impossible de casser un script écrit pour une ancienne version. Pensez à la tendance récente vers l'UTF-8 comme un encodage de fichier texte. Ce changement a nécessité de changer les utilitaires de base comme tr. Dans le passé, le texte simple était presque toujours ASCII (ou quelque chose de proche), donc les lettres majuscules formaient une plage numérique, tout comme les lettres minuscules. Ce n'est plus vrai avec UTF-8, donc traccepte différentes options de ligne de commande pour spécifier des choses comme "majuscules" ou "alphanumériques".

  4. L'une des meilleures façons de «durcir» vos filtres est de ne pas dépendre d'une disposition de texte particulière. Par exemple, ne le faites pas cut -c10-24, ce qui dépend des positions d'une ligne. Utilisez-le à la cut -f2place, ce qui supprimerait le deuxième champ séparé par des tabulations. awkcasse toute ligne d'entrée en $ 1, $ 2, $ 3 ... qui sont des espaces blancs séparés par défaut. Dépendre de concepts de niveau supérieur tels que "champs" plutôt que de concepts de niveau inférieur comme position de colonne. En outre, utilisez des expressions régulières: sedet awkpouvez à la fois faire des choses avec des expressions régulières qui ne se soucient pas d'une certaine variation de l'entrée. Une autre astuce consiste à traiter l'entrée en quelque chose dont le format de votre filtre peut être difficile. Permet tr -cs '[a-zA-z0-9]' '[\n]'de diviser le texte en un seul mot par ligne, sans ponctuation. Tu ne fais que

Bruce Ediger
la source
9

Tout d'abord, des réponses très brèves à vos questions:

  1. Normalisation formelle des conventions d'entrée / sortie: non
  2. Rupture dans le passé due à un changement de sortie: oui
  3. Absolument impossible de briser les futurs filtres: non
  4. Comment puis-je me protéger contre les changements: soyez conservateur

Lorsque vous dites "API", vous utilisez un terme qui (pour le meilleur ou pour le pire) implique trop de formalité autour des conventions d'entrée / sortie du filtre. Très (et je veux dire "très") de façon générale, les principales conventions pour les données qui sont faciles à filtrer sont

  • chaque ligne d'entrée est un enregistrement complet
  • dans chaque enregistrement, les champs sont séparés par un délimiteur connu

Un exemple classique serait le format de / etc / passwd. Mais, ces conventions par défaut sont probablement violées dans une certaine mesure plus souvent qu'elles ne sont suivies à la lettre.

  • Il existe de nombreux filtres (souvent écrits en awk ou perl) qui analysent les formats d'entrée multilignes.
  • Il existe de nombreux modèles d'entrée (par exemple, / var / log / messages) où il n'y a pas de structure de champ bien définie, et des techniques plus générales basées sur des expressions régulières doivent être utilisées.

Votre quatrième question, comment vous protéger contre les variations de la structure de sortie, est vraiment la seule à laquelle vous pouvez faire quoi que ce soit.

  • Comme l'a dit @ jw013 , regardez ce que disent les normes posix. Bien sûr, posix ne spécifie pas toutes les commandes que vous voudrez utiliser comme sources d'entrée.
  • Si vous voulez que vos scripts soient portables, essayez d'éviter les idiosyncrasies de la version quelle que soit la commande que vous avez installée. Par exemple, de nombreuses versions GNU des commandes Unix standard ont des extensions non standard. Ceux-ci peuvent être utiles, mais vous devez les éviter si vous souhaitez une portabilité maximale.
  • Essayez d'apprendre quels sous-ensembles d'arguments de commandes et de formats de sortie ont tendance à être stables sur toutes les plateformes. Malheureusement, cela nécessite l'accès à plusieurs plates-formes avec le temps, car ces différences ne seront notées nulle part, même de manière informelle.

En fin de compte, vous ne pouvez pas vous protéger complètement des problèmes qui vous inquiètent et il n'y a pas un seul endroit où chercher pour une déclaration "définitive" de ce qu'une certaine commande devrait faire. Pour de nombreux scripts shell, en particulier ceux écrits pour une utilisation personnelle ou à petite échelle, ce n'est tout simplement pas un problème

Dale Hagglund
la source
5

Ne couvrant que 1) de votre question.

Naturellement, les API peuvent toujours changer à la volonté de leurs créateurs, et donc casser les logiciels dépendants, dans n'importe quelle langue. Cela dit, la grande idée des "API" d' E / S des outils Unix est qu'il n'y en a pratiquement pas (peut-être0x0a en fin de ligne). Un bon script filtre les données avec les outils Unix au lieu de les créer. Cela signifie que votre script peut se casser parce que les spécifications d'entrée ou de sortie ont changé, mais pas parce que le format d'E / S (encore une fois, il n'y en a pas vraiment un) des outils individuels utilisés dans le script a changé (parce que quelque chose qui n'existe pas vraiment ne peut pas vraiment changer).

En parcourant une liste d'outils de base, il y en a peu que j'attribuerais également producteur , par opposition au seul filtre:

  • wc - affiche le nombre d'octets, de mots, de lignes - format très simple, donc absolument improbable de changer, et de plus peu susceptible d'être utilisé dans un script.
  • diff - il y a eu différents formats de sortie mais je n'ai entendu aucun problème. Également pas normalement utilisé sans supervision.
  • Date - Maintenant, ici, nous devons vraiment faire attention à ce que nous produisons, en particulier en ce qui concerne les paramètres régionaux du système. Mais sinon le format de sortie est RFC étant donné que vous ne le spécifiez pas exactement vous-même.
  • cal - ne parlons pas de cela, je sais que le format de sortie diffère beaucoup d'un système à l'autre.
  • ls , qui , w , dernier - je ne peux pas aider si vous voulez analyser ls, ce n'était pas censé être. En outre, qui, w, last, sont des listers plus interactifs; Si vous les utilisez dans un script, vous devez faire attention à ce que vous faites.
  • le temps a été souligné dans un autre post. Mais oui, c'est la même chose qu'avec ls. Plus pour une utilisation interactive / locale. Et le bash intégré est très différent de la version GNU, et la version GNU a des bogues non corrigés depuis de nombreuses années. Ne vous y fiez pas.

Voici des outils qui attendent un format d'entrée particulier plus spécifique qu'un flux d'octets:

  • bc , dc - calculatrices. Déjà du côté plus hackish des choses (vraiment, je ne les utilise pas dans les scripts), et des formats d'E / S vraisemblablement très stables.

Il existe un autre domaine avec un risque de rupture beaucoup plus élevé, à savoir l'interface de ligne de commande. La plupart des outils ont des fonctionnalités différentes à la fois sur les systèmes et sur la chronologie. Des exemples sont

  • Tous les outils utilisant regex - regex peuvent changer de signification en fonction des paramètres régionaux du système (par exemple LC_COLLATE) et il existe de nombreuses subtilités et particularités dans les implémentations de regex.
  • N'utilisez tout simplement pas de commutateurs sophistiqués. Vous pouvez facilement utiliser, man 1p findpar exemple, pour lire la page de manuel de recherche POSIX au lieu de la page de manuel du système. Sur mon système, j'ai besoin que manpages-posix soit installé.

Et même lorsque vous utilisez de tels commutateurs, normalement, aucune erreur ne sera subtilement introduite et n'empoisonnera vos données. La plupart des programmes refusent simplement de fonctionner avec un commutateur inconnu.

Pour conclure, je dirais que le shell a en fait le potentiel d'être l'un des langages les plus portables (il est portable lorsque vous scriptez de manière portable). Comparez-les à vos langages de script préférés où des erreurs subtiles se produisent, ou à votre programme compilé préféré qui cèdera à la compilation.

De plus, aux rares endroits où une rupture peut se produire en raison d'incompatibilités, cela ne serait probablement pas dû au temps induit, mais à la diversité des différents systèmes (ce qui signifie que si cela fonctionne pour vous, il l'a fait 20 ans auparavant et le sera dans 20 ans , aussi). C'est un corollaire de la simplicité des outils.

Jo So
la source
1

Il n'y a que des normes d'E / S de facto - espaces blancs et sorties séparées par des valeurs nulles.

En ce qui concerne la compatibilité, nous revenons généralement à la vérification des numéros de version des filtres individuels. Non pas qu'ils changent beaucoup, mais lorsque vous souhaitez utiliser une toute nouvelle fonctionnalité et que le script s'exécute toujours sur des versions plus anciennes, vous devez le "ifdef" le supprimer d'une manière ou d'une autre. Il n'y a pratiquement pas de mécanisme de rapport de capacité, sauf pour l'écriture manuelle de cas de test.

lynxlynxlynx
la source
0

Les scripts cassent, certains plus souvent que d'autres. L'ancien et célèbre logiciel a tendance à rester relativement le même et présente souvent des indicateurs de compatibilité lorsqu'il change de toute façon.

Les scripts écrits sur un système continuent de fonctionner, mais en cassent souvent un autre.

Alex Chamberlain
la source