Journal Tu souhaites apprendre à programmer en shell

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
48
17
août
2012

Cher lecteur, tu souhaites apprendre à programmer le shell. Voici quelques recommendations que tu trouveras utiles (ou non).

(Ceci est une version modifiée d'un de mes commentaires planqué au fond du forum. Quand j'ai pensé au fantastique appeau à troll que j'avais écrit, j'ai décidé d'en faire un journal—c'est trolldi pour encore au moins deux longues heures!)

Usenet

Pour apprendre à programmer il faut lire beaucoup de programmes: abonne-toi à comp.unix.shell tu apprendras vite!

Bashing

Comme bash est un gros shell bien gras, tu peux réfléchir à apprendre sh plutôt que bash. L'avantage est que tu programmeras de façon un peu plus portable — ce qui peut toujours s'avérer utile. Pour la programmation, le gros plus de bash, c'est les tableaux… quelque part j'ai envie de dire que le shell ne sert pas à programmer, mais à décrire un workflow, et que le besoin de tableau témoigne d'un problème qui ne doit pas être résolu dans le shell. Mais je ne critique pas ceux qui veulent mettre plus loin la frontière de complexité des problèmes qui appartiennent au domaine du shell, c'est aussi une affaire de goûts.

En gros l'idée est que si tu utilises beaucoup ton tableau, il vaut peut-être mieux écrire un petit outil pour résoudre ton problème. Sinon, tu peux très bien coller le cotenu du tableau dans un fichier, voire dans un dossier (chaque fichier contient la valeur d'
d'une cellule).

Littérature

Ce livre est souvent controversé, mais je te le recommande tout de même: Unix shell programming de Lowell Jay Arthur (2nd édition, c'est important car souvent dans ce genre de livre, les éditions varient substantiellement). Ce livre est très imprégné de la philosophie Unix et si tu le comprends bien, tu sais bien à quoi sert le shell.

Le point clef est que le shell sert à décrire un workflow et que les programmes que tu coordonnes (avec des pipes) doivent travailler sur une représentation commune des données, la plus simple possible. (XML et JSON sont beaucoup trop complexes, par exemple!)

Certains outils (rares) sont très shell-friendly:

onsgmls (James Clark) permet de passer d'un document SGML à une version validée et facile à traiter en shell (chaque élément, attribut, etc. apparaît sur sa propre ligne).

noweb (Norman Ramsey) est un outil de literate porgramming qui utilise un format externe trivial pour pouvoir piper ses documents vers un filtre Unix donné par l'utilisateur, qui va enrichir le documents (par exemple en énumérant les variables définies dans un code snippet). (Ce qui au passage te fait une source d'exemples de scripts awk, car la plupart de ces filtres sont écrits avec awk)

svndump, représente un repository subversion dans un format facile à manipuler (à ceci près que les données sont très interconnectées!)

Le point clef de la programmation shell est donc la création de programmes élémentaires qui travaillent sur une représentation commune des données, si possible assez simple pour éventuellement être travaillée avec sed, awk, … (cela évite d'avoir à coder des analogue de awk et sed pour la représentation en question).

Quelques défauts répandus

La sous-utilisation du système de fichiers. Par exemple, le plus simple pour implémenter une table associative dans le shell est d'utiliser le système de fichiers, Lowell Jay Arthur explique ça très bien.

La sous-utilisation des pipes nommés et des IPC.

L'utilisation de echo lorqu'il faut utiliser printf (echo sert à afficher un message pour l'utilisateur, tout le reste doit être géré par printf).

Quelques conseils pour se simplifier la vie

Pour toutes tes interpolations de type backquote un tant soit peu compliquée, écris une fonction auxiliaire: tu éviteras toutes sortes de migraines liées aux échappements et aux quotes!

Cale toutes tes boucles while read dans une fonction à part que tu inséreras dans ta séquence de pipes, cela te simplifiera aussi la vie!

Outils pour les thématiques de base

En gros les thématiques de bases sont:

  • fichiers: date (pour les noms horodatés), mv, cp, rm, tar, find, xargs, mktemp ou sa variante qui marche.

  • base de données: join, paste, etc. (pour préparer ton input à awk! et trop souvent méconnus)

  • filtres: sort, sed, awk, cut, head, tail, grep.

  • portabilité: command.

Ensuite viennent les outils correspondant au domaine de ton problème:

  • unix: stat, ps, id, /etc/passwd, etc.

Enfin tout le monde croit que make sert à compiler des programmes alors qu'en fait écrire un makefile est aussi une autre façon de programmer en shell en utilisant un point de vue très différent.

Bon courage et amuse-toi bien!

(PS la seconde édition du livre de Lowell Jay Arthur est complètement obsolète — je crois qu'elle parle de System V si bien qu'aucun des exemples du livre ne marche directement, cela te fera travailler!)

  • # Merci

    Posté par  . Évalué à 7.

    Toujours sympa d'avoir des références pour ce genre de choses. Tu peux expliquer pourquoi favoriser la deuxième édition ? Je vois sur Amazon qu'on en est déjà à la quatrième.

    Aussi, pourquoi ce livre est-il sujet à polémique ?

    Merci bien

    • [^] # Re: Merci

      Posté par  (site web personnel) . Évalué à 4.

      Toujours sympa d'avoir des références pour ce genre de choses.

      Avec plaisir!

      Tu peux expliquer pourquoi favoriser la deuxième édition ?

      C'est celle que j'ai lue. Comme je le dis, ce genre de livre es souvent substantiellelemnt remanié d'une édition à l'autre et des choses très intéressantes sont parfois remplacées par d'autres, moins intéressantes…

      Aussi, pourquoi ce livre est-il sujet à polémique ?

      Je pense que c'est parcequ'il souvent approximatif sur les questions techniques. Mais le plus important dans ce livre ce sont les principes généraux de programmation plutôt que les détails techniques. Cela en fait un livre un peu inhabituel car beaucoup de livres se bornent à accumuler des recettes techniques sans vraiment faire de mise en perspective. Ici c'est tout le contraire, si je puis dire!

  • # Tableau

    Posté par  . Évalué à 7.

    En gros l'idée est que si tu utilises beaucoup ton tableau, il vaut peut-être mieux écrire un petit outil pour résoudre ton problème. Sinon, tu peux très bien coller le cotenu du tableau dans un fichier, voire dans un dossier (chaque fichier contient la valeur d'une cellule).

    Je pense que c'est une « rétro-explication », bourne shell était trop limité pour gérer les tableaux et on a trouvé une explication un peu moins pittoresque pour l'expliquer. Ce qui montre pour moi clairement que les tableaux manque aux bourn shell c'est la variable $PATH par exemple. On a pas de tableau donc on utilise une chaîne avec des délimiteurs.

    Comme bash est un gros shell bien gras, tu peux réfléchir à apprendre sh plutôt que bash.

    Je pense qu'il est important de parler de bourn shell plutôt que de « sh ». Tu parle à quelqu'un qui débute si sa distribution utilise encore bash comme shell par défaut ça ne va pas lui apporter grand chose.

    Plus personnellement, j'écris du bourn shell, du bash et du zsh. Le bourne shell quand je ne connais pas la configuration cible (mais dans ce cas il est important de vérifier les usages que l'on a des outils tel que grep, cut et sed qui contiennent une foule d'extensions GNU, on perd tout l'intérêt d'utiliser un shell POSIX. La variable POSIXLY_CORRECT est importante pour ça ainsi que les man).
    bash quand je sais que j'en ai un à disposition sur la cible.
    zsh quand c'est un script que je fais vite pour m'aider moi.

    Pour toutes tes interpolations de type backquote un tant soit peu compliquée, écris une fonction auxiliaire: tu éviteras toutes sortes de migraines liées aux échappements et aux quotes!

    Il me semble que même avec POSIX les $() sont accepté (il n'y à guère que sur linuxfr que je m'en sert du `).

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Tableau

      Posté par  (site web personnel) . Évalué à 3.

      Je pense que c'est une « rétro-explication »

      Dans mon commentaire je n'explique pas à quel point c'est une bonne idée de ne pas avoir de tableaux en shell, mais plutôt que cela fournit un indicateur de la complexité des problèmes que l'on peut utiliser pour décider si le shell doit être utilisé pour ŕesoudre le problème ou non.

      Je pense qu'il est important de parler de bourn shell plutôt que de « sh ».

      C'est vrai que j'aurais pu me donner la peine d'être un peu plus clair, merci de souligner ce point!

    • [^] # Re: Tableau

      Posté par  . Évalué à 1.

      Je pense que c'est une « rétro-explication », bourne shell était trop limité pour gérer les tableaux et on a trouvé une explication un peu moins pittoresque pour l'expliquer.

      Il y a des tableaux : "$@". Il suffit de faire une fonction pour en avoir. Le soucis c'est plutôt le découpage des mots… Perso, j'essaie de caler la constitution et le traitement du tableau dans awk quand j'en ai vraiment besoin…

      Je pense qu'il est important de parler de bourn shell plutôt que de « sh ».

      En fait, non. Le Bourne shell contenait plein de choses obsolètes. Aujourd'hui il vaut mieux parler de shell POSIX. Le meilleur shell pour s'y initier est ÀMHA dash (« branche Xu », la branche Debian est différente).

      Le bourne shell quand je ne connais pas la configuration cible (mais dans ce cas il est important de vérifier les usages que l'on a des outils tel que grep, cut et sed qui contiennent une foule d'extensions GNU, on perd tout l'intérêt d'utiliser un shell POSIX.

      Tout à fait, ça n'a pas de sens d'écrire du shell POSIX si les commandes ne suivent pas. La norme est d'ailleurs publique.

      Bon après, il y a aussi des trucs qui ne sont pas dedans, mais présents partout ou presque: "local" (le gros manque, selon moi), "find -(min|max)depth", …

      Il me semble que même avec POSIX les $() sont accepté

      Oui. D'ailleurs, je trouve la syntaxe bien meilleure, puisque c'est bien la sortie d'un sous-shell qu'on récupère et pas d'une commande. Ainsi, on peut écrire un truc du genre (pour récupérer le chemin absolu du dossier contenant un fichier) :

      x="$(cd "$(dirname "$fichier")"; pwd))"
      
      
      • [^] # Re: Tableau

        Posté par  (site web personnel) . Évalué à 2. Dernière modification le 18 août 2012 à 10:37.

        Il y a des tableaux : "$@". Il suffit de faire une fonction pour en avoir

        Tu as raison, merci de cette correction!

        x="$(cd "$(dirname "$fichier")"; pwd))"
        
        

        Je profite de cet exemple pour illustrer ce que je disais sur les backquotes.

        adirname()
        {
           local d
           d=`dirname "$1"`
           (
             cd "$d"; pwd
           )
        }
        
        x=`adirname "$fichier"`
        
        

        À noter que, il me semble, ta proposition explose si dirname $fichier contient des espaces. C'est beaucoup plus facile d'écrire des backquotes expansions robustes en les isolant dans des petites procédures.

        • [^] # Re: Tableau

          Posté par  . Évalué à 2.

          À noter que, il me semble, ta proposition explose si dirname $fichier contient des espaces. C'est beaucoup plus facile d'écrire des backquotes expansions robustes en les isolant dans des petites procédures.

          Non, $fichier est entre guillemets, donc dirname fonctionne, lui même entre guillemets, qui permet à cd de fonctionner.

          Par contre, la dernière parenthèse fermante est de trop. :)

          Et les guillemets qui entourent l'expression de premier niveau sont inutiles (je les mets par tic, car avec "local x=$()" il les faut absolument si l'expansion retourne plusieurs mots).

        • [^] # Re: Tableau

          Posté par  . Évalué à 3. Dernière modification le 18 août 2012 à 10:58.

          Ce qui me gène avec les backquotes, c'est la diminution de la lisibilité du code source (partant de sa compréhension)

          un $( ) est beaucoup plus visible et explicite qu'un ` , et plus on utilise de backquotes plus cela semble s'aggraver.

          Sinon quels sont les autres avantages techniques de l'un et l'autre ?

          • [^] # Re: Tableau

            Posté par  . Évalué à 5.

            Le $() peut avoir d'autres $() à l'intérieur alors que les backquotes non.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: Tableau

              Posté par  . Évalué à 2.

              ah je regrette, les backquotes peuvent être imbriquées, il suffit simplement de les protéger par un backslash au premier niveau 3 au deuxième ou quatre je sais plus… Enfin a coup de TAF empirique (Try And Fail) on finit par trouver le bon nombre…

              Bon depuis que j'ai découvert le $() je n'utilise plus les backquotes ;)

              Il ne faut pas décorner les boeufs avant d'avoir semé le vent

              • [^] # Re: Tableau

                Posté par  (site web personnel) . Évalué à 3.

                Une autre méthode, plus lisible que l'échappement et l'imbrication, consiste à les coller dans une fonction à part.

                https://linuxfr.org/users/chat_de_sorciere/journaux/tu-souhaites-apprendre-a-programmer-en-shell#comment-1380013

                À la différence de C++ où il faut commencer une psychothérapie à chaque fois qu'on veut définir une fonction, l'introduction de nouvelles fonctions est très facile en shell. Profitons-en pour coder clairement!

                • [^] # Re: Tableau

                  Posté par  . Évalué à 3.

                  À la différence de C++ où il faut commencer une psychothérapie à chaque fois qu'on veut définir une fonction

                  Je sais que l’exagération est voulue, mais définir une fonction en C++ n'est pas plus difficile que définir une fonction en… C. Donc si ton idée est de dire que définir une fonction dans un langage de script quelconque est plus simple que pour les langages compilés classiques bon ben, nous sommes bien d'accord. Et encore, c'est discutable.

                  /* En C/C++ */
                  int sqr(int x) { return x*x; }
                  
                  

                  ou

                  (* en OCaml *)
                  let sqr x = x * x
                  
                  

                  me semblent plus sympa que

                  # En Bash
                  sqr()
                  {    
                      sqr_result=$(($1*$1))
                  }
                  #do something about sqr_result
                  
                  
                  • [^] # Re: Tableau

                    Posté par  (site web personnel) . Évalué à 1.

                    Je sais que l’exagération est voulue,

                    J'éxagère un peu, mais pas tant que ça!

                    Pour commencer tu n'as pas déclaré les prototypes de tes fonctions. Ensuite ton exemple est beaucoup trop naïf, quand tu programmes en C++ il faut absolument répondre aux questions suivantes:

                    1. Comment passer mes arguments à ma fonction? J'ai le choix entre: par valeur immédiate, par référence, par pointeur sans transfert de propriété, par pointeur avec transfert de propriété, par recopie (si mon objet définit un constructeur de recopie, et que celui-ci n'est pas cassé… ah mais au fait, c'est une copie superficielle, etc.) Et encore j'évite les hypothèses de type pointeur sur void avec information adjascente pour récupérer le type (c'est parfois une option sérieuse).

                    2. Comment récupérer la valeur de ma fonction? Dans un argument que j'aurais moi-même passé en argument (par référence ou par pointeur?) ou bien dans une valeur de retour, ou bien je renvoie une référence ou un pointeur sur un espace de stockage privé?

                    3. Comment signaler les erreurs?

                    Et encore je ne pose pas la question de savoir s'il est pertinent de fixer certains arguments comme template alors que c'est une question aprfois importante (par exemple pour les booléens gouvernant les messages de debugging et de profiling).

                    mais définir une fonction en C++ n'est pas plus difficile que définir une fonction en… C.

                    Je pense avoir démontré le contraire, puisque C n'a ni référence ni exception, les réponses possibles aux questions précédentes sont limitées à un choix plus restreint. Je parlais de C++ et pas de C, de toutes façons.

                    • [^] # Re: Tableau

                      Posté par  . Évalué à 2.

                      Pour commencer tu n'as pas déclaré les prototypes de tes fonctions.

                      Tu n'es pas obligé de déclarer les prototypes. La norme autorise à ne pas le faire car une définition fait aussi office de déclaration.

                      Comment passer mes arguments à ma fonction? J'ai le choix entre: par valeur immédiate, par référence, par pointeur sans transfert de propriété, par pointeur avec transfert de propriété, par recopie (si mon objet définit un constructeur de recopie, et que celui-ci n'est pas cassé… ah mais au fait, c'est une copie superficielle, etc.) Et encore j'évite les hypothèses de type pointeur sur void avec information adjascente pour récupérer le type (c'est parfois une option sérieuse).

                      Non. Tu fais ce que tu veux en C++. Tu n'es même pas obligé de passer par le système orienté objet si tu n'aimes pas. Tu peux parfaitement faire du « C en C++ » avec juste l'avantage d'avoir un typage plus fort et oui, des références à la place des pointeurs quand ça a du sens.

                      Comment récupérer la valeur de ma fonction? Dans un argument que j'aurais moi-même passé en argument (par référence ou par pointeur?) ou bien dans une valeur de retour, ou bien je renvoie une référence ou un pointeur sur un espace de stockage privé?

                      Alors qu'en Bash, c'est tellement mieux : tu es obligé de déclarer une variable extérieure à ta fonction, qui vit dans un espace de nommage « global » et donc risque d'entrer en conflit avec d'autres variables (bref, c'est de ta faute si deux fonctions utilisent la/les même(s) nom(s) de variable(s) de retour).

                      Ensuite, tu utilises tout un vocabulaire compliqué (« espace de stockage privé » ? Vraiment ?). Il y a deux solutions globalement : soit tu renvoies une valeur avec ta fonction, soit tu écris dans une variable passée par référence (que ce soit une référence de type « pointeur » ou « référence C++ », ça reste du passage par référence). Ensuite, n'importe quel codeur C (même pas C++) sait que dès que le type devient un peu complexe, il est préférable (le plus souvent, il y a toujours des exceptions bien entendu) de passer un paramètre par référence (donc par pointeur en C, pointeur ou de préférence référence en C++). Bref, tu as en gros trois choix pour le retour :

                      type f(paramètres...);               // par valeur, pour les toutes petites structures ou les types primitifs
                      void f(paramètres..., type& retour); // pour tout le reste en gros
                      void f(paramètres..., type* retour); // à utiliser en dernier recours, si on a besoin de manipuler le pointeur lui-même
                      
                      

                      Tout ce qui est qualification de type const, volatile, etc., rentre dans la notion de « type » dans ce contexte.
                      Je suis le même raisonnement général pour le passage de paramètres pour une fonction en C ou C++.

                      Comment signaler les erreurs?

                      Tu fais comme en C : tu utilises un entier/un enum pour le retour de ta fonction. Tu n'es pas obligé de passer par les exceptions. Tu n'es jamais obligé d'utiliser l'intégralité d'un langage, et c'est encore plus vrai en C++ où Stroustrup, Meyers, Sutter & co ne cessent de répéter qu'en C++ on ne paie que pour ce qu'on utilise. Rien ne t'oblige à faire de la méta-programmation à base de templates ou même de faire des fonctions templates si tu n'en as pas besoin. Rien ne t'oblige à faire du « C avec classes » si tu ne le veux pas. Et si tu le veux, rien ne t'empêche de jeter l'encapsulation par la fenêtre si tu veux juste un POD : déclare simplement des structures comme en C (avec l'avantage de pouvoir quand même définir des fonctions membres si tu veux faire un peu d'objet).

                      Évidemment, C++ est bien plus complexe que Bash, je ne le nie pas. Mais quand on me dit qu'en Bash il est bien plus simple de déclarer des fonctions qu'en C++, je suis très moyennement d'accord. Je me dis même qu'il suffirait de se donner une règle très simple pour « émuler » en partie la façon dont les fonctions Bash fonctionnent : toujours passer ses paramètres par référence constantes (types primitifs inclus), et si la fonction renvoie quelque chose, toujours fournir une/des variables passées elles aussi par référence.

                      Tu m'aurais dit « en Python/en Ruby, il est bien plus facile de déclarer des fonctions qu'en C++ », j'aurais déjà été plus d'accord. Ce qui me gêne dans les fonctions Bash, c'est l'absence de portée lexicale pour les valeurs de retour.

                      • [^] # Re: Tableau

                        Posté par  (site web personnel) . Évalué à 2.

                        Non. Tu fais ce que tu veux en C++. Tu n'es même pas obligé de passer par le système orienté objet si tu n'aimes pas.

                        OK, mais là on parle de programmation en C++ pas d'un sous-ensemble de C++. Ta proposition de ne pas utiliser les classes est une façon parmi d'autres de choisir une stratégie pour répondre aux questions que je pose.

                        Si tu programmes sans le côté orienté objet, cela ne va même pas te dispenser de la question des opérateurs de recopie et de la question de propriété d'une valeur transmise par poitneur ou par réfeŕence.

                        Alors qu'en Bash, c'est tellement mieux : tu es obligé de déclarer une variable extérieure à ta fonction, …

                        Ta fonction écrit sur stdout ou dans un fichier si c'est une valeur complexe ou renvoie un code de retour. Tu récupères la valeur avec '>' ou avec les backquotes pour les valeur complexes. Le shell ne sert pas à manipuler ces valeurs, il sert à coordonner le travail de plusieurs processus (en leur passant les valeurs).

                        Ensuite, tu utilises tout un vocabulaire compliqué (« espace de stockage privé » ? Vraiment ?).

                        Tu peux appeler comme tu veux, mais c'est le retour de std::string::c_str et std::string::data.

                        Ensuite, n'importe quel codeur C (même pas C++) sait que dès que le type devient un peu complexe, il est préférable (le plus souvent, il y a toujours des exceptions bien entendu) de passer un paramètre par référence (donc par pointeur en C, pointeur ou de préférence référence en C++). Bref, tu as en gros trois choix pour le retour

                        La différence avec ce que j'ai écrit est que tu as supprimé l'opération de recopie. Si tu passes un pointeur avec transfert de propriété et que tu veux continuer à travailler sur la valeur, il faut la recopier. Quelle que soit la façon dont tu résous le problème, tu n'as pas le droit de le passer sous silence.

                        [passage des erreurs]
                        Tu fais comme en C : tu utilises un entier/un enum pour le retour de ta fonction.

                        Tu as déjà entendu parler de la variable [errno]? En C il n'y a pas de stratégie canonique de gérer les erreurs, en C++ c'est pire.

                        Tu n'es pas obligé de passer par les exceptions.

                        C'est une stratégie de réponse aux questions que je pose. En plus elle est mauvaise, parceque des fonctions de la bibliothèque standard lancent des exceptions, et des fonctions d'autres biliothèques peuvent le faire: on ne peut pas complètement ignorer les exceptions. Ensuite si tu veux programmer vaguement sérieusement, tu veux écrire des opérations transactionelles (ta structure est toujours dans un état bien défini où les invariants sont satisfaits).

                        Tu n'es jamais obligé d'utiliser l'intégralité d'un langage

                        Travailler avec un sous-ensemble de C++ est une stratégie de réponse aux questions que je pose.

                        Mais quand on me dit qu'en Bash il est bien plus simple de déclarer des fonctions qu'en C++, je suis très moyennement d'accord.

                        Tu n'es pas d'accord parceque dans tes explications, tu as complètement perdu de vue le problème de la gestion de la mémoire. Le protocole de gestion de la mémoire fait partie de la définition d'une fonction en C++. C'est une différence fondamentale entre le C++ et des langages de haut niveau ou le shell.

                        • [^] # Re: Tableau

                          Posté par  . Évalué à 3.

                          OK, mais là on parle de programmation en C++ pas d'un sous-ensemble de C++. Ta proposition de ne pas utiliser les classes est une façon parmi d'autres de choisir une stratégie pour répondre aux questions que je pose.

                          Pas d'accord. Pour paraphraser S.Meyers (« Effective C++ »), le C++ est en réalité 4 langages en un :

                          1. Le langage C
                          2. Le langage « C avec classes » (et potentiellement en 2b. C avec classes + exceptions + RTTI)
                          3. C + STL (c'est très, TRÈS utilisé dans pas mal de machins pour simulations numériques soit dit en passant)
                          4. Le système de templates (en incluant les possibilité de méta-programmation) + C

                          Tu peux prendre indépendamment n'importe lequel de ces langages, ou bien en faire une combinaison. Pour donner un exemple à la con : j'utilise C++ pour l'écriture d'un runtime. Nous utilisons le système OO, les templates, et nous avons utilisé la STL principalement pendant la phase de prototypage (nous essayons de remplacer les conteneurs STL par des structures de données qui s'adaptent mieux au contexte — à l'exception de std::string, bien pratique). Nous n'utilisons pas les exceptions. Jamais. Nous ne pouvons pas nous permettre de payer pour exceptions et potentiellement la RTTI qui va avec.

                          Ta fonction écrit sur stdout ou dans un fichier si c'est une valeur complexe ou renvoie un code de retour. Tu récupères la valeur avec '>' ou avec les backquotes pour les valeur complexes.

                          Ah bah forcément, si tu limites les fonctions du shell à ce qui t'arrange … :-) Mais oui, je suis bien d'accord, le shell n'est que le « squelette » (en gros : structures de contrôle et notion de « statement »). Que le reste soit fait à base d'utilisation de bc, seq, etc. ne me choque pas du tout dans le cadre de Bash. Ce que je dis, c'est que la notion de « fonction » en Bash est en réalité une notion de « procédure » (i.e. qui ne renvoie aucun résultat). Et si tu veux pouvoir communiquer ce résultat à d'autres fonctions dans ton script Bash, ou tout bêtement à l'appelant, tu te retrouve obligé de passer par des variables globales prédéfinies et connues de l'utilisateur de la fonction. Si toutes tes fonctions Bash sont purement autonomes je suis d'accord ça suffit. Si tu cherches à aller plus loin (je ne dis pas que c'est souhaitable), la notion de fonction en Bash est très limitée.

                          Le shell ne sert pas à manipuler ces valeurs, il sert à coordonner le travail de plusieurs processus (en leur passant les valeurs).

                          Ben plein de gens ne sont pas d'accord avec toi, sinon la notation $(()) pour faire de l'arithmétique entière n'existerait pas, par exemple (et je m'en suis servi plusieurs fois pour automatiser des benchmarks dont les binaires prenaient en paramètres des valeurs numériques …). Ou les fonctions « builtin », etc.

                          Cela dit, c'est justement parce que programmer en C/C++/ADA/blah est trop chiant pour ce qui concerne la « coordination de processus », mais que (ba)sh est trop limité dans beaucoup de cas (le script de coordination commence à dépasser la trentaine de lignes ;-)) que des langages comme Perl émergé.

                          [à propos du passage de paramètre en C/C++] La différence avec ce que j'ai écrit est que tu as supprimé l'opération de recopie. Si tu passes un pointeur avec transfert de propriété et que tu veux continuer à travailler sur la valeur, il faut la recopier. Quelle que soit la façon dont tu résous le problème, tu n'as pas le droit de le passer sous silence.

                          Donc en gros, tu m'expliques que si je fais des choses plus compliquées que Bash, alors j'ai plus de questions à me poser quant à la façon de concevoir mes fonctions en C++. Je dis que si tu veux rester au « même niveau » de fonctionnalités que Bash pour les appels de fonction, un moyen simple est de faire faire du « tout référence constante » pour le passage de paramètre, et « tout référence » (non-const) pour écrire le résultat de la fonction (ça marche aussi en passant un std::ostream).

                          Pour la copie, etc., j'aimerais bien que tu me donnes un exemple simple de ce que tu veux dire en utilisant Bash et C++. Les copies profondes ou superficielles ne sont un problème que si tu manipules des objets complexes. Dans le cas des appels de fonction en C++, je ne vois pas le rapport. Ta fonction se fout de la sémantique de copie. Elle prend des paramètres en entrée, et renvoie éventuellement un résultat, point.

                          • [^] # Re: Tableau

                            Posté par  (site web personnel) . Évalué à 2.

                            Pas d'accord.

                            Pas d'accord avec quoi? Ton choix es un choix, non?

                            Tu peux prendre indépendamment n'importe lequel de ces langages…

                            C'est à dire prendre une décision qui va m'aider à repondre aux questions que je pose sur la déclaration des fonctions.

                            Ah bah forcément, si tu limites les fonctions du shell à ce qui t'arrange … :-)

                            Je ne limite rien du tout, je programme le shell comme dans la vraie vie. Par exemple, si tu manipules des fichiers XML ou SGML, tu ne vas pas t'amuser à stocker les représentations intermédiaires dans une variable bash—vu que de toutes façons, écrire une fonction sh qui fasse quoique ce soit sur cette donnée. La manipulation des données complexes est réalisée par des des programmes externes. Si tu n'a pas encore compris ça, tu n'as pas encore compris à quoi sert le shell.

                            Ben plein de gens ne sont pas d'accord avec toi, sinon la notation $(()) pour faire de l'arithmétique entière n'existerait pas, par exemple

                            Je suis très content qu'on puisse faire +1 en bash, ce dont je parle dans la citation c'est des données complexes, type document XML, arbre binaire, champ de données. Tout ça est invisible au shell qui se contente d'organiser le flux de données entre les processus.

                            Donc en gros, tu m'expliques que si je fais des choses plus compliquées que Bash, alors j'ai plus de questions à me poser quant à la façon de concevoir mes fonctions en C++.

                            Ce que je te dis c'est que dans C++ tu dois gérer la mémoire à la main, pas en shell. Donc si tu programmes en C++ sans te soucier des question mémoires, tu ne sais pas programmer en C++.

                            Je dis que si tu veux rester au « même niveau » de fonctionnalités que Bash pour les appels de fonction,

                            Ah bah forcément, si tu limites l'usage du C++ à ce qui t'arrange … :-)

                            J'ai écrit ça

                            À la différence de C++ où il faut commencer une psychothérapie à chaque fois qu'on veut définir une fonction

                            Si tu as décidé entre temps que le contexte était d'écrire une fonction C++ qui fasse la même chose qu'une fonction bash donnée, c'est ton affaire, mais cela ne m'engage pas.

                            Dans le cas des appels de fonction en C++, je ne vois pas le rapport. Ta fonction se fout de la sémantique de copie. Elle prend des paramètres en entrée, et renvoie éventuellement un résultat, point.

                            Comment ça la fonction s'en fout? Je ne parle pas de la fonction mais du développeur qui doit écrire le prototype de la fonction. Lui ne s'en fout pas du tout car sinon c'est le SEGFAULT assuré! LA question de la recopie est toujours la question de la possession d'une structure ou non.

                          • [^] # Re: Tableau

                            Posté par  (site web personnel) . Évalué à 2.

                            Nous n'utilisons pas les exceptions. Jamais. Nous ne pouvons pas nous permettre de payer pour exceptions et potentiellement la RTTI qui va avec.

                            Vous peut-être pas mais le système le fait (bad_alloc). Si vous choisissez d'ignorer délibérement cela, c'est une stratégie parmi d'autres pour (ne pas) gérer les erreurs en C++.

                            Les exceptions et la RTTI sont des mécanismes indépendants.

            • [^] # Re: Tableau

              Posté par  . Évalué à 3.

              Il me semble qu'il y a également comme différence ceci : avec les backquotes ça lance un autre shell pour exécuter la commande, alors qu'avec $() c'est le shell courant qui l'exécute.

              Je ne sais pas dans quels cas cela peut avoir une incidence mais il doit bien y en avoir.

              • [^] # Re: Tableau

                Posté par  . Évalué à 5.

                Il me semble qu'il y a également comme différence ceci : avec les backquotes ça lance un autre shell pour exécuter la commande, alors qu'avec $() c'est le shell courant qui l'exécute.

                C'est le cas pour les deux, si je ne dis pas trop de bêtises.

                Je ne sais pas dans quels cas cela peut avoir une incidence mais il doit bien y en avoir.

                Par exemple :

                VAR=1
                $(VAR=3)
                echo "$VAR" # 1
                
                

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                • [^] # Re: Tableau

                  Posté par  . Évalué à 2.

                  Après lecture du manuel de bash, il semble que tu ais raison. Je ne sais pas d'où m'est venu cette idée saugrenue.

      • [^] # Re: Tableau

        Posté par  (site web personnel) . Évalué à 1.

        "dirname" "basename", c'est tout de même beaucoup plus court et performant en bash (pas de fork pour lancer un binaire extérieur "à la con") :

        dirname=${fichier%/*}
        basename=${fichier##*/}

        (bien pratique pour les message d'erreurs , ex :
        echo "${0##*/}: Erreur: vous n'avez pas mangé de goulbi boulga !" ; exit 42 )

        Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

        • [^] # Re: Tableau

          Posté par  . Évalué à 1.

          Je t'inutile non pas parce que c'est faux, mais parce que ça n'a pas beaucoup de rapport avec le sujet.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Tableau

          Posté par  . Évalué à 1.

          (bien pratique pour les message d'erreurs , ex :
          echo "${0##*/}: Erreur: vous n'avez pas mangé de goulbi boulga !" ; exit 42 )

          C'est dommage d’exécuter une expression régulière à chaque fois. Il vaut mieux mettre le ${0##*/} dans une variable.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

          • [^] # Re: Tableau

            Posté par  (site web personnel) . Évalué à 1.

            Évidemment, si on utilise l'expression régulière plusieurs fois …

            Mais dans mon exemple : soit tu ne l'as pas bien lu, soit tu dois m'expliquer ce que tu ré-utilises après un exit !? :-p

            Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

            • [^] # Re: Tableau

              Posté par  . Évalué à 2.

              Je n'avais pas vu le exit, mais pour moi le message d'erreur pré exit devrait être un message de log comme un autre (avec une une importance supérieure).

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Tableau

          Posté par  . Évalué à 8. Dernière modification le 19 août 2012 à 11:45.

          "dirname" "basename", c'est tout de même beaucoup plus court et performant en bash (pas de fork pour lancer un binaire extérieur "à la con") :

          Sauf qu'il faut que tu sois sûr de la tête du chemin en argument. Exemple :

          #!/bin/sh
          echo "-- Built-in --"
          echo "dirname  [${1%/*}]"
          echo "basename [${1##*/}]"
          echo ""
          echo "-- Commandes --"
          echo "dirname  [$(dirname "$1")]"
          echo "basename [$(basename "$1")]"
          
          
          $ ./dir.sh ../toto/
          -- Built-in --
          dirname  [../toto]
          basename []
          
          -- Commandes --
          dirname  [..]
          basename [toto]
          
          $ ./dir.sh /lib                                 
          -- Built-in --
          dirname  []
          basename [lib]
          
          -- Commandes --
          dirname  [/]
          basename [lib]
          
          $ ./dir.sh /
          -- Built-in --
          dirname  []
          basename []
          
          -- Commandes --
          dirname  [/]
          basename [/]
          
          

          Si je fais la commande que j'ai montrée plus loin, dans certains cas je vais travailler dans mon ~. Si le dossier cible est censé être temporaire dans le script… ^_^'

          De plus, j'ai eu quelques surprise avec les regex internes. Je me disais aussi que c'était beaucoup plus rapide (je stockais une immense chaine avec toutes les données, dont j'éliminais/récupérais certains éléments à coup de regex), et bien non, pour les gros trucs sed semble faire du meilleur boulot.

          • [^] # Re: Tableau

            Posté par  (site web personnel) . Évalué à 1.

            C'est pas faux, mais :
            - Cela pourrait être corrigé en virant les '/' en trop à la fin de la variable avant la substitution, et en remplaçant le résultat par / si la substitution retourne une chaîne vide.
            - Surtout ces cas d'utilisations ne m'arrive jamais (et je pense que je ne suis pas le seul) : je n'ai jamais utilisé de dirname ou basename sur un paramètre en entré du programme (autre que $0 qui est toujours formaté comme il faut pour ${0##*/}) : si c'est des éléments du système de fichier, c'est en général pour y accéder (en lecture ou écriture), pas pour les analyser.

            (Et donc je me contente de choses du genre

            cd "$1" || exit 1
            function < "$1" || exit 2 )

            Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

            • [^] # Re: Tableau

              Posté par  . Évalué à 1.

              - Cela pourrait être corrigé en virant les '/' en trop à la fin de la variable avant la substitution, et en remplaçant le résultat par / si la substitution retourne une chaîne vide.

              Bien sûr, mais ça revient à réimplémenter dirname et basename en shell. À moins vraiment de les utiliser beaucoup dans le script et de ne pas pouvoir traiter tous les chemins en même temps via un tube, il y peu à gagner.

              Surtout ces cas d'utilisations ne m'arrive jamais (et je pense que je ne suis pas le seul)

              Je ne dis pas que c'est systématique, mais ça m'arrive assez souvent d'avoir des chemins en paramètres, en variables d'environnement (les trucs type "ROOT" ou "TMPDIR"), ou en définition dans un fichier de conf. Dès lors que c'est l'utilisateur qui entre le chemin, tu ne peux rien supposer.

          • [^] # Re: Tableau

            Posté par  (site web personnel) . Évalué à 2.

            #!/bin/sh
            echo "-- Built-in --"
            echo "dirname [${1%/*}]"
            echo "basename [${1##*/}]"

            Donnez moi un caillou, donnez moi un caillou ! ;-)

            Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

  • # *sh VS. python

    Posté par  (site web personnel) . Évalué à 1.

    Ton journal est très intéressant et je vais me plonger dans les références citées dès tout de suite, mais j'ai une question.

    Je me suis mis au Python il y a quelques mois et je trouve le langage diablement puissant lorsqu'il s'agit de scripter le système. Puissant et simple.

    Dès lors, en face à face avec un script sh plus classique, quels seraient les avantages de l'un par rapport à l'autre (au delà du fait qu'un shell *sh est toujours présent, alors que l'interpréteur Python ne l'est pas forcément) ?

    There is no spoon...

    • [^] # Re: *sh VS. python

      Posté par  (site web personnel) . Évalué à 10.

      L'avantage du shell à mon avis c'est que c'est le même langage pour scripter que pour faire de la ligne de commande. En fait, un script python c'est un petit programme alors qu'un script shell n'est qu'une grosse ligne de commande++.

      Donc pour quelqu'un comme moi qui n'aime pas spécialement programmer mais qui aime bien la ligne de commande, le shell est un choix naturel et agréable. Alors que pour python tu ne peux pas utiliser naturellement ta connaissance du système et de la ligne de commande pour scripter.

      Après il est certain que python est plus puissant plus généraliste et surtout beaucoup moins dégueulasse… Par contre il faut franchir un pas important pour commencer à l'apprendre alors qu'un simple fichier

      cd rep
      echo lol > ficher
      
      

      est déjà un script shell.

    • [^] # Re: *sh VS. python

      Posté par  (site web personnel) . Évalué à 2.

      Bash :

      a=1
      a=$(( $a + 1 ))
      
      

      Opposé au Python :

      a=1
      a+=1
      
      

      (il me semble que la syntaxe a++ n'est pas acceptée en Python).

      J'utilise bash et je me retrouve avec des drôles de syntaxe pour faire des opérations arithmétiques et des comparaisons, juste parce que le paradigme est que tout est texte (et commandes). Je crois qu'en sh pur, c'est pire.

      Commentaire sous licence LPRAB - http://sam.zoy.org/lprab/

      • [^] # Re: *sh VS. python

        Posté par  (site web personnel) . Évalué à 2.

        lol, bash est bien plus puissant que cela : dès que tu mes qqch entre double parenthèse tu peux utiliser exactement la même syntaxe qu'en C pour manipuler des int (seulement). De plus le $ n'est plus nécessaire à l'intérieur (sauf pour les paramètre du programme : $1 , $2 … évidemment)

        ex :

        ((a+=1))
        ((a++))
        ((++a))
        marche aussi très bien /* note qu'il y avait tout de même un bug à une époque puisque "++a" se comportait exactement de la même façon que "a++", je ne sais pas si ça a été résolu depuis */

        Tu peux même utiliser l’opérateur conditionnel "?:" , mais aussi les %, &, &&, |, ||, &= ….

        exemple :

        echo $((a++%2?42:666))

        Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

    • [^] # Re: *sh VS. python

      Posté par  . Évalué à 4.

      J'utilise ipython comme shell en remplacement de zsh et je dois dire que j'en suis très satisfait. Je trouve le Bourne Shell lent, limité et archaïque.

      • [^] # Re: *sh VS. python

        Posté par  . Évalué à 6.

        Je plussois sur le fait que le bourne shell est limité et archaïque (pour sa vitesse j'en sais rien et j'm'men fou). C'est d'ailleurs intéressant, on (moi inclut) utilise le bourne shell car il est POSIX et qu'on c'est que ça fonctionnera pareil partout (tout unix disons), mais les langages de scripts « classiques » (python, ruby et perl) remplissent très bien leur office (surtout python et perl qui sont quasiment toujours présent de base dans un unix). À noter tout de même que la gestion des compatibilité entre les versions de python réduit de beaucoup l'effet « ça marche partout pareil ».

        Pour ce qui est d'utiliser ipython comme shell, ça n'est envisageable que parce qu'il intègre quelques fonctions de base pour ça (cd, pwd, etc). Il faudrait se les écrire si elles n'existaient pas. Mais même comme ça je ne suis pas prêt de quitter mon zsh qui me permet régulièrement de faire ça :

        alias mmv='noglob zmv -W' # repris tel quel de la doc (c'est une fois pour toute dans mon .zshrc)
        mmv -p convert *.xbm *.png
        
        

        C'est avec ce genre de truc que je me dis que le shell (zsh) rox des pandas pour le traitement de fichier par lot.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: *sh VS. python

          Posté par  (site web personnel) . Évalué à -1.

          Moi je moinssoie, car qui utilise encore du bourn shell (sh) ?

          À part certains dinosaures crampés à leur HP-UNIX ou AIX qui ne tarderont pas à disparaître
          Ou d'autres dans l'embarqué qui le gardent comme shell par défaut, soit par "méchanceté" (les allergiques à la GPLv3 comme Android pour qui en plus faire des applis autrement qu'en java pour Davlik, c'est mal…), soit par bêtise (d'autres qui installeront ensuite de gros python ou un perl (mdr…) pour faire des scripts que l'on pourrait très bien faire en bash ou zsh.

          Avec la puissance des systèmes embarqués actuels, il vaut mieux y installer un bon vieux bash des familles que de rester en sh (à moins qu'on est vraiment pas la place, mais c'est ni le cas des set top boxes, ni celui des smartphones).

          Enfin le bash, alors qu'il est très largement déployé est encore plus généralement énormément sous-employé (En gros la plupart, même chez les "pros" qui font des distribs ou maintiennent des système en prod pour des grosses boîtes très connues, ne l'utilisent qu'à moins de 50% de ses capacités).

          Ex: combien connaissent/maîtrisent les "<(commande )", "exec", "eval", "trap", "| while IFS=":" read champ1 reste", "=~" (regex pour le test de bash) ?

          Perso, depuis que je maîtrise suffisamment les bash modernes, je ne vois plus aucun intérêt d'utiliser perl (et donc python que je n'ai jamais vraiment appris, mais la plupart disent qu'ils répondent au même besoin).

          Le seul inconvénient que je vois à bash, c'est en effet que les features ont été empilées comme on peut, donc ça peut faire crade et surtout ça devient illisible pour la plupart quand on les exploitent pleinement.

          (l'avantage de cet inconvénient, et qu'un programme bash peut être beaucoup plus court que le même en python ou en perl).

          Exemples de programmes bash "que c'est moi qu'ai fait ça" :
          - un bot logger irc : https://github.com/Open-UDC/open-udc/blob/master/scripts/irclogger.sh
          - un outil pour faire des bots web avec lynx (je m'en servais pour faire des bots qui jouent à ma place à certains MORPG en php, … et je pourrais m'en servir pour faire des bots qui me plussoie sur dlfp ! ;-)) : https://github.com/jbar/LynxBot

          ps: pour zsh, j'avais essayé un temps, c'est vrai qu'il avait des choses sympas que bash n'a pas, mais aussi quelques réciproques vraies, et la chose qui m'en a éloigné : c'est qu'il est pas (presque) partout par défaut.

          ps2: je n'ai pas encore pris l'habitude de tester la version de bash dans mes programmes, et ça peut être mal quand j'utilise des features récentes…

          Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

          • [^] # Re: *sh VS. python

            Posté par  . Évalué à 2.

            ps2: je n'ai pas encore pris l'habitude de tester la version de bash dans mes programmes, et ça peut être mal quand j'utilise des features récentes…

            C'est possible à faire (sans devoir parser la sortie de bash --version)?
            Je m'étais déjà dit que ce serait une bonne idée (car je travaille avec des machines qui ont des versions différentes), mais je n'avais pas trouvé.

            • [^] # Re: *sh VS. python

              Posté par  (site web personnel) . Évalué à 5.

              echo $BASH_VERSINFO (juste le numéro majeur de version).
              echo $BASH_VERSION

              Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

          • [^] # Re: *sh VS. python

            Posté par  . Évalué à 2.

            Moi je moinssoie, car qui utilise encore du bourn shell (sh) ?

            Personne ou presque, mais ceux qui veulent faire du portable (ou du pas trop non-portable) font du shell POSIX, qui ne propose pas tout ce que bash/ksh/zsh proposent.

            À part certains dinosaures crampés à leur HP-UNIX ou AIX qui ne tarderont pas à disparaître
            Ou d'autres dans l'embarqué qui le gardent comme shell par défaut, soit par "méchanceté"[…]

            Ou parce que pour le prix d'un binaire bash, tu as une p* de Busybox (tu sais le truc qui met toutes les commandes dans un seul binaire).

            […]pour faire des scripts que l'on pourrait très bien faire en bash ou zsh.

            Et voilà bien le problème, comme tu les dis en post-scriptum c'est bash ou zsh. Les développeurs bash/zsh/ksh n'ont pas tous fait les même choix en terme d'extension. À l'inverse, bash exécuté en tant que sh doit se comporter en shell POSIX, idem pour zsh, idem pour ksh.

            (je te plussoie/redéplioie parce qu'au fond c'est ton opinion… et que je ne moinse jamais personne… ;)

          • [^] # Re: *sh VS. python

            Posté par  . Évalué à 6.

            Moi je moinssoie, car qui utilise encore du bourn shell (sh) ?

            Debian avec Dash à l'init (ils en ont d'ailleurs bien chier pour virer les bashisme) pour des raison de vitesse et d'empreinte mémoire.

            « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

            • [^] # Re: *sh VS. python

              Posté par  (site web personnel) . Évalué à -1.

              Debian qui préfère pousser un truc "POSIX" sous licence BSD, qu'un truc GNU sous GPLv3 8-o … c'est la fin des haricots !

               Pour des raisons de vitesse...
              
              

              J'attend de voir cela, d'ailleurs je n'ai trouvé aucun bench sur le web !? (si t'as de bons liens je suis preneur).
              Je suis particulièrement sceptique sur ce point puisque les "bashism" permettent souvent d'éviter des appels à sed, awk, expr, ou de faire des choses en beaucoup moins d'Itérations.
              Est-ce que le temps pour bash d'identifier la syntaxe est si long par rapport au temps gagné ensuite par ces bashism ?

              Par contre je jette moi aussi avec enthousiasme la pierre à ceux qui faisaient des "bashism" en tout gardant le shebang #!/bin/sh.
              (putain quoi, sh est tellement limité, c'est pas compliqué d'identifier son périmètre…:evil:)

              J'espère seulement qu'ils vont savoir se limiter à POSIX et ne pas rajouter de "dashism" au fur et à mesure…

              Enfin revenir à du "pur" shell POSIX, me semble pas une bonne approche, je vois cela vraiment comme un simple retour en arrière plutôt qu'une évolution. On peut critiquer systemd (moi-même je ne suis pas convaincu par son implémentation), mais c'est une approche déjà plus innovante, on peut aussi regarder comment font les mac, on peut aussi rêver d'un vrai compilateur bash (ou sh dans un premier temps) afin de ne plus du tout utiliser d'interprété au démarrage …

              Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

              • [^] # Re: *sh VS. python

                Posté par  . Évalué à 6.

                Debian qui préfère pousser un truc "POSIX" sous licence BSD, qu'un truc GNU sous GPLv3 8-o … c'est la fin des haricots !

                Debian suit la DSFG comme toujours :) Mais Debian n'est pas la seule à être passé à dash (ubuntu a était la première).

                J'attend de voir cela, d'ailleurs je n'ai trouvé aucun bench sur le web !? (si t'as de bons liens je suis preneur).
                Je suis particulièrement sceptique sur ce point puisque les "bashism" permettent souvent d'éviter des appels à sed, awk, expr, ou de faire des choses en beaucoup moins d'Itérations.
                Est-ce que le temps pour bash d'identifier la syntaxe est si long par rapport au temps gagné ensuite par ces bashism ?

                Non. Pour exécuter un script POSIX bash est plus lent que dash.

                Par contre je jette moi aussi avec enthousiasme la pierre à ceux qui faisaient des "bashism" en tout gardant le shebang #!/bin/sh.
                (putain quoi, sh est tellement limité, c'est pas compliqué d'identifier son périmètre…:evil:)

                C'était le cas il n'y a pas si longtemps.

                J'espère seulement qu'ils vont savoir se limiter à POSIX et ne pas rajouter de "dashism" au fur et à mesure…

                C'est le principe du projet donc je ne vois pas pourquoi il ne le feraient pas.

                Enfin revenir à du "pur" shell POSIX, me semble pas une bonne approche, je vois cela vraiment comme un simple retour en arrière plutôt qu'une évolution.

                Ça permet d'éviter au système d'init d'avoir une dépendance vers bash. C'est dommage de forcer la main vers bash alors qu'il existe d'autres shell au moins aussi pertinents (zsh, ksh et tcsh entre autre).

                On peut critiquer systemd (moi-même je ne suis pas convaincu par son implémentation), mais c'est une approche déjà plus innovante […]

                Le but n'est pas l’innovation, mais simplement d'améliorer l'existant.

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: *sh VS. python

                Posté par  . Évalué à 3.

                Est-ce que le temps pour bash d'identifier la syntaxe est si long par rapport au temps gagné ensuite par ces bashism ?

                Je n'ai pas l'impression que ces bashism puissent être très utilisés lors de l'init. Dans ce cas, c'est surtout la vitesse de chargement (suite aux nombreux fork) et d'exécution qui prime.

                Il faut savoir aussi que dash dépend aussi de moins de bibliothèque, ce qui est plus pratique pour un système d'exploitation universel.

                « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

                • [^] # Re: *sh VS. python

                  Posté par  . Évalué à 3.

                  Je n'ai pas l'impression que ces bashism puissent être très utilisés lors de l'init. Dans ce cas, c'est surtout la vitesse de chargement (suite aux nombreux fork) et d'exécution qui prime.

                  Si si je pense que les bashisms sont utilisables mais pas souhaitables lors de l'init.

                  Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: *sh VS. python

                Posté par  . Évalué à 3.

                Debian qui préfère pousser un truc "POSIX" sous licence BSD, qu'un truc GNU sous GPLv3 8-o … c'est la fin des haricots !

                S'ils se basent sur la branche Xu, il n'y a pas de guillemets à mettre à POSIX, c'est plutôt en deçà de la norme qu'au delà (ie. si ce n'est pas explicitement requis, ce n'est pas supporté).

                Je suis particulièrement sceptique sur ce point puisque les "bashism" permettent souvent d'éviter des appels à sed, awk, expr, ou de faire des choses en beaucoup moins d'Itérations.

                C'est faux. Plus il y a d'itérations, plus tu gagnes à utiliser une commande dédiée. Exemple :

                #!/bin/sh
                while read l; do
                    echo "${l%9}"
                done
                
                
                $ time seq 0 9999 | dash bench.sh > /dev/null                                                             
                    0m0.11s real     0m0.08s user     0m0.02s system
                $ time seq 0 9999 | bash bench.sh >/dev/null
                    0m0.45s real     0m0.40s user     0m0.04s system
                
                
                #!/bin/sh
                sed 's/\(.*\)9$/\1/;'
                
                
                $ time seq 0 9999 | bash /tmp/bench.sh  >/dev/null
                    0m0.05s real     0m0.04s user     0m0.00s system
                $ time seq 0 9999 | dash /tmp/bench.sh  >/dev/null
                    0m0.05s real     0m0.04s user     0m0.00s system
                
                

                On voit que dash est plus rapide que bash, mais que les deux restent en deçà d'un sed bien placé.

                Enfin revenir à du "pur" shell POSIX, me semble pas une bonne approche, je vois cela vraiment comme un simple retour en arrière plutôt qu'une évolution.

                Ce n'est pas revenir, c'est rester dans la norme. Honnêtement, les extensions me paraissent plus être des facilités que des réformes du langage. Quand le shell POSIX trouve ses limites (on peut faire beaucoup mais, je le reconnais, pas tout), je préfère coder en Perl qu'utiliser des extensions propres à un shell particulier.

                • [^] # Re: *sh VS. python

                  Posté par  . Évalué à 2.

                  LinuxMag avait un article il n'y a pas si longtemps qui « benchait » le contraire. Faudrait que je regarde de plus près.

                  Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                  • [^] # Re: *sh VS. python

                    Posté par  . Évalué à 2.

                    Oui, ce serait intéressant. :)

                    Perso, je teste tous mes scripts avec dash, et j'ai toujours constaté qu'il était plus rapide que bash, ou même mksh (mon shell de prédilection). Après, c'est vrai que côté debug , c'est pas le top, ces rapports d'erreur étant assez sommaires en comparaison de shells plus "évolués".

                    • [^] # Re: *sh VS. python

                      Posté par  . Évalué à 3.

                      Tiens je ne connais pas du tout mksh qu'as t'il de particulier ?

                      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                      • [^] # Re: *sh VS. python

                        Posté par  . Évalué à 1.

                        En fait, je pourrais me contenter d'un shell POSIX, mais malheureusement dash n'a pas de complétion, ce qui le rend impropre à une utilisation en shell interactif (on peut néanmoins avoir un historique en le liant à libedit). Mksh à côté propose historique et complétion tout en en étant d'expérience plus rapide et moins gros que bash (avec en prime pas mal d'extensions Korn Shell, comme les tableaux). J'ai jamais essayé (gros flemmard que je suis), mais je pense qu'il n'y aurait pas trop de soucis à l'utiliser en tant que sh en substitut à bash.

                        J'aime bien aussi le ash de Busybox (on peu compiler juste ça et obtenir un binaire de 80K avec historique et complétion), mais j'ai eu quelques surprises en UTF-8 (j'ai l'impression que ce n'est pas leur priorité). Avec un "set -o utf8-mode", mksh fonctionne au contraire comme un charme.

                        Donc, pour résumer, mksh me parait un bon compromis quand on cherche juste un shell POSIX quotidien "moderne". :)

                        • [^] # Re: *sh VS. python

                          Posté par  . Évalué à 2.

                          Je t'aurais bien parlé de rlwrap mais il fait pas d'auto complétion et fait 68Kio.

                          Personnellement je gagne plus de temps avec simplement les abréviation que me permet zsh (Ig va donner "| grep ", Ia -> "| awk ", Is, etc, l'auto-cd qui me permet de ne pas mettre de cd, les répertoires nommés et les raccourcis zle avec par exemple Echap+q qui efface la commande en cour de frappe et la ré affiche une foie que j'ai tappé une commande, etc), plutôt qu'au lancement de mon login shell et aux actions un peu plus rapides.

                          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                          • [^] # Re: *sh VS. python

                            Posté par  . Évalué à 2. Dernière modification le 21 août 2012 à 19:32.

                            Je t'aurais bien parlé de rlwrap mais il fait pas d'auto complétion et fait 68Kio.

                            C'est sympa comme concept, je ne connaissais pas. C'est bête cependant, il complète les chemins, mais pas les commandes.

                            Personnellement je gagne plus de temps avec simplement les abréviation que me permet zsh (Ig va donner "| grep ", Ia -> "| awk ", Is

                            Et tu emploies ça comment ? tu appuies sur une touche pour expanser (basename $x Ig^A -> basename $x | grep [suite de la frappe…]), ou zsh convertit automatiquement les mots qui correspondent des abréviations (basename $x Ig x$ -> basename $x | grep x$) ? dans la seconde hypothèse, ça ne fait pas trop de conflits entre abréviations et arguments de commande ?

                            • [^] # Re: *sh VS. python

                              Posté par  . Évalué à 2.

                              C'est avec un espace que ça se fait. J'utilise des abréviations peu courantes Ig seul avec un espace ensuite ne se rencontre jamais pareil pour Ia (avec cette casse là). Tu peux vouloir commencer toutes tes abréviations par __ comme ça tu n'a pas de risque de te planter. :-)

                              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                • [^] # Re: *sh VS. python

                  Posté par  (site web personnel) . Évalué à 0.

                  while read l; do
                  echo "${l%9}"
                  done

                  Je ne comprend plus rien : c'est POSIX cela ? (depuis quand ?)
                  Ou bien c'est dash qui fait déjà plus que le POSIX requis ?

                  Je moinsse, tu moinsses, il moinsse, nos bots moinssent ...

          • [^] # Re: *sh VS. python

            Posté par  (site web personnel) . Évalué à 2.

            Moi je moinssoie, car qui utilise encore du bourn shell (sh) ?

            Le fait que bash soit nouveau signifie que les scripts shells qui ont été écrits avant que son usage ne se répande n'ont pas été écrits en bash. Cela fait une belle palanquée de programmes à maintenir et développer.

    • [^] # Re: *sh VS. python

      Posté par  . Évalué à 2.

      La question pourrait aussi se poser avec perl (et la réponse serait la même). La manipulation du système de fichier et d'autres programme est du domaine du shell. Les autres langages font tout le reste. Pour t'en convaincre le mieux c'est de prendre un script shell existant est de le traduire.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: *sh VS. python

      Posté par  (site web personnel) . Évalué à 3.

      Dès lors, en face à face avec un script sh plus classique, quels seraient les avantages de l'un par rapport à l'autre

      La différence est que le Shell occupe une place centrale dans le travail sous Unix, donc si tu décides de travailler de façon adaptée au shell, tu vas reçupérer plein de bénéfices (en termes de flexibilité et d'intégration avec le reste du système)… mais ce n'est malheureusement pas gratuit!

      Disons que tu écrives un système pour gérer les livres d'une bibliothèque. Tu vas probablement utiliser une base de données, puis définir des procédures du genre
      inserer_livre, enlever_livre, pareil pour les utilsiateurs, rechercher emprunter, reserver, renre, gérer la file des retards, etc. À un moment de ta conception va venir la question de l'interface utilisateur. C'est à ce moment là que tu décides d'intégrer ton travail au shell ou non.

      Soit tu décides de caler toutes ces fonctions dans un programme permettant de réaliser les opérations les plus communes.

      Soit tu décides de définir une format extérieur pour les types de données les plus important de ton programme ce qui te permet d'écrire chaque fonction citée plus haut comme un programme individuel (tu peux utiliser Python ou ce que tu veux pour cela, et ce n'est même pas obligé d'utiliser le même langage partout!)

      La deuxième solution demande un peu plus de travail (surtout parcequ'elle te demande de définir des formats extérieurs). L'avantage est que l'utilisateur de ton programme peut définir ses propres worklfows et y insérer facilement les procédures de son choix avec la langage de son choix.

  • # un exemple en passant

    Posté par  . Évalué à 2.

    Enfin tout le monde croit que make sert à compiler des programmes alors qu'en fait écrire un makefile est aussi une autre façon de programmer en shell en utilisant un point de vue très différent.

    c'est pas forcement très orienté "programmation" comme utilisation, mais un très bon exemple pour une autre utilisation de make se trouve dans "Time Management for System Administrators" de T. Limoncelli.

    que du bonheur, les bouquins de cet auteur!

    • [^] # Re: un exemple en passant

      Posté par  (site web personnel) . Évalué à 4.

      À noter qu'il est disponible en français sous le titre « Admin'sys : Gérer son temps ».

      C'est vrai que ça vision du makefile est intéressante. Aujourd'hui je l'utilise (aussi) pour faire des scènes povray, faire un pdf à partir d'un texte balisé…

      • [^] # Re: un exemple en passant

        Posté par  (site web personnel) . Évalué à 4.

        C'est vrai que ça vision du makefile est intéressante. Aujourd'hui je l'utilise (aussi) pour faire des scènes povray, faire un pdf à partir d'un texte balisé…

        Ce sont des travaux de compilation classiques non?

        • [^] # Re: un exemple en passant

          Posté par  (site web personnel) . Évalué à 5.

          Ce sont des travaux de compilation classiques non?

          Ok, je donne un exemple un peu plus précis. Récemment je voulais générer un graphe dot, dont chaque nœud est une image. Les images devant être générées dynamiquement.

          1. Les outils du shell me servent de liant pour extraire le nom de chaque nœud et y associer l'image que je veux générer

          2. Grâce aux dépendances de make, je génère le fichier povray quivabien (en remplaçant une chaine de texte dans mon source) :

            temp/%.pov: board.pov
            cat board.pov | sed -e "s/\$${config}/$(patsubst temp/%.pov,%,$@)/" > $@

            (Je vois au passage un joli uuoc. Écrire le commentaire m'aura permis de me relire :))

          3. Je génère ensuite l'image par povray :

            img/%.png: temp/%.pov
            ${POVRAY} +A0.3 +W250 +H250 $(patsubst img/%.png,temp/%.pov,$@)
            mv $(patsubst img/%.png,%.png,$@) img

          4. Il ne me reste plus qu'à lancer dot, en faisant quelques substitutions pour référencer les images qui viennent d'être générées.

          Au final toutes mes images ont été générées via make. Grand luxe, make me supprime même les fichiers povray puisqu'il a détecté qu'il s'agit de fichiers intermédiaires, n'ayant aucune utilité dans mon rendu final ! Et en mettant à jour mon fichier dot, les nouvelles images sont automatiquement générées.

  • # Bashing ?

    Posté par  . Évalué à 2.

    Est-ce que l'utilisation de tableaux est réellement du «bashing» ?
    Quitte à utiliser un anglicisme, ne serait-ce pas un «bashism» ?

    • [^] # Re: Bashing ?

      Posté par  . Évalué à 4.

      en tout cas c'est une vrai basherie ;)

      Il ne faut pas décorner les boeufs avant d'avoir semé le vent

    • [^] # Re: Bashing ?

      Posté par  (site web personnel) . Évalué à 2.

      Est-ce que l'utilisation de tableaux est réellement du «bashing» ?

      J'ai utilisé ce mot comme titre à un paragraphe qui commence par «comme bash est un gros shell bien gras…» je me suis donc risqué au jeu de mots.

  • # Livre en français?

    Posté par  . Évalué à 1.

    Avez-vous un livre en français à conseiller?

    • [^] # Re: Livre en français?

      Posté par  . Évalué à 3.

      J'aime bien Scripts sous linux et Langages de scripts sous linu tout les deux de Christophe Blaess.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Livre en français?

        Posté par  . Évalué à 1.

        Merci pour l'info. On me pose souvent la question mais le bouquin qui m'avait servi (et me sert encore parfois) n'est plus édité…
        Je vais les noter pour la prochaine fois où on me le demandera!

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.