Journal Gufo: un langage de shell moderne!

Posté par  . Licence CC By‑SA.
27
7
déc.
2017

Sommaire

Gufo est un prototype de langage de script moderne qui peut embarquer des commandes unix et servir de shell.

Pourquoi

Bash est, pour les utilisateurs techniques, l'interface texte majeure sous les systèmes GNU-linux.

La raison principale est la facilité à appeler des programmes externes et la possibilité de combiner des flux d'entrées/sorties dans l'environnement orienté fichier de GNU-linux.

En gardant conscience de la qualité et de la quantité de travail qui a été mise dans Bash, un système d'exploitation moderne mérite un langage de shell utilisant les techniques de compilation et d'interprétation modernes. En dehors de la simple exécution d'une ligne de commande, il est nécessaire de disposer d'un langage de shell permettant d'exécuter des scripts pouvant manipuler facilement des lignes de commandes (par exemple "nb_files=ls -l | wc -l"). Bash le permet mais d'une manière tout à fait archaïque.

Modestement, mais avec une certaine envie de voir ce genre d'évolution, j'ai commencé à imaginer un nouveau langage pour le shell. J'ai travaillé à cette idée à temps partiel depuis septembre 2017 (l'idée, je l'avais depuis plus longtemps). Le projet est loin d'être déjà utilisable mais peut être considéré comme une "preuve de concept" (proof of concept) permettant d'annoncer les idées principales qui seront utilisées et d'obtenir des retours.

Je considère les fonctionnalités suivante comme étant importante dans un langage 'moderne':

  • Un système de typage avancé:

    • la possibilité d'utiliser des tuples, listes, map et set (ensemble) en plus des types de bases.
    • une des innovations de Gufo est de considérer une commande shell comme un type de base.
    • la possibilité de créer ses propres types "struct".
    • le typage est "statique implicite": le programmeur ne déclare pas les types, ceux-ci sont inférés par l'interpréteur.
  • Un travail important de vérification pré-exécution:

    • Gufo est un langage interprété (bien logiquement pour un langage de shell) mais il fait des vérification sur le code avant de lancer l'exécution effective: l'idée est que l'on veut éviter autant que possible les échecs durant les exécutions (parce que l'utilisateur risque de perdre du temps si l'exécution du script est longue, ou pire risque de mettre le système dans un état inconsistant). C'est pourquoi, Gufo vérifie en amont de l'exécution que chaque variable est bien défini (à l'intérieur d'un scope), que les appels de fonctions sont consistent et fait une vérification de typage (type-checking).

Dans l'état actuel de Gufo, beaucoup reste à faire, entre autre, il reste des questions sur l'utilisation des entrées/sorties (comment paramétrer leur affichage entre sortie standard et/ou stockage dans une variable), messages d'erreur correct mais sans aucune information de position dans le code, travail à faire pour rendre l'usage console agréable… L'objectif à terme est de fournir une version libre, mais je ne ferais cela que si le projet est suffisamment avancé au moins pour une utilisation basique ce qui n'est pas le cas aujourd hui. Cela dépend aussi des opportunités de financement du projet.

Une description utilisateur

Le meilleur moyen de gouter à Gufo, est de plonger dans quelques exemples compréhensibles aussi bien pour des débutants que pour des utilistateurs avancés.

Une simple commande

code

 ls

explication
Une simple commande shell est un programme Gufo valide, c'est ce qui lui permettra d'être un language de shell. La commande ls permet de lister les fichiers du répertoire courant. Ce programme permettra donc l'affichage de cette liste.

Une commande avec redirection

code

ls -l | wc -l > nb_files.txt

explication
Gufo gère les opérateurs de redirections du bash. Pour information, "|" redirige la sortie de la première commande ("ls -l") vers la deuxième commande ("wc -l"). ">" redirige la sortie du résultat vers le fichiers nb_files.txt.
La encore, le but est d'imiter bash, tout en reconnaissant qu'on ne le fait que pour les opérateurs simples, sans aller dans les fonctionnalités avancées (et souvent occultes).

Gufo permet aussi à l'utilisateur d'interagir avec l'entrée standard dans le cas de programme interactif.

Stockage d'une commande dans une variable

code

let $a = ls -l | wc -l

explication
Une variable en Gufo est toujours préfixé par un '\$' pour qu'il n'y ai pas ambigüité avec une commande. 'let' est le mot clé permettant de déclarer une variable ou une fonction.
Le résultat de la commande (les système unix renvoit 0 pour une exécution réussi ou un entier non-nul indiquant l'erreur) est atteignable via "\$a.Cmd.res", le contenu du résultat via "\$a.Cmd.print_std".

Une fonction basique

code

let $factoriel $c = 
  if ($c != 1) 
  then $c * ($factoriel ($c - 1))
  else 1

explication

Voici la fonction factoriel (qui ne dépend pas d'éventuelles lignes de commandes). Cette fonction prend un argument "\$c", si celui-ci est différent de 0, la fonction s'appelle récursivement (avec \$c décrémenté à chaque appel). La syntaxe, et de nombreux aspect du langage sont empruntés à Ocaml. Gufo est un langage fonctionnel (généralement pas d'effet de bord, une fonction prend des arguments et renvoi une valeur résultante).

Une fonction avancé

code

let $run $fichier =
  let $current_log = $fichier+".log" in(
  let $past_log = $fichier+".log.old" in(
  #  put current log as past past log
  let $as_log = cat $current_log > $past_log in (
    if $as_log.Cmd.res == (some 0) 
      then (./gufo_run.native $fichier > $current_log; diff -q $current_log $past_log)
      else (echo "create first log"; ./gufo_run.native $fichier > $current_log)
  )))

explication

Voici d'abord une explication générale: le but de cette fonction est d'exécuter le programme externe "./gufo_run.native" prenant en paramètre une chaine de caractères (contenu dans '\$fichier') et de stocker son résultat dans le fichier référé par '\$current_log'. dans le cas ou la commande à déjà été exécuté on veut afficher le diff par rapport au log de l'exécution précédente.

Voila le détails par ligne:

  1. déclaration de la fonction '\$run' qui prend 1 argument '\$fichier'. Les lignes suivantes représente le corps de la fonction.
  2. déclaration de la variable '\$current_log' qui correspond à la concaténation de '\$fichier' avec la chaine ".old". Le "in" indique la porté, délimiter par la paire de parenthèses, dans laquelle est utilisable la variable.
  3. similaire à la ligne précédente, pour définir un "log.old".
  4. un commentaire (commence toujours par # et se termine en fin de ligne).
  5. exécution de la commande 'cat \$current_log > \$past_log', le résultat est mis dans la variable '\$as_log'.
  6. un if et sa condition: la condition est vrai si le résultat de la commande vaut 0 (le "some" sera détaillé mais permet de différencier un cas ou l'exécution de la commande s'est effectivement produit, d'un cas ou celle-ci ne ce serait pas produit (on aurait un "none).
  7. l'exécution dans le cas ou la condition est vérifié: exécute le programme "gufo_run.native avec comme argument '\$fichier' en mettant le résultat dans le fichier indiqué par '\$current_log' On exécute successivement la commande diff.
  8. l'exécution dans le cas ou la condition n'est pas vérifiée: exécution d'une commande "echo" suivi de l'execution de la commande "./gufo_run.native".
  9. fermeture des portés de variables ouvertes.

Divers

Il y aurait de nombreuses autres fonctionnalités à montrer, elles seront présentés dans un tutoriel plus complet (en particulier, concernant les tuples, listes, set et map…). Voila simplement un dernier exemple pour expliquer qu'il y a un système de type "personnalisé", semblable au struct du C:

code

struct $mystruct= {
  var1: string ,
  var2: cmd,
  var3: $myotherstruct,
  var4: string -> int,
}

explication
On a défini un type '\$mystruct' disposant de quatre champs nommés et respectivement typés. Le champs "var3" a pour type une autre structure, le champs "var4" à pour type une fonction prenant une chaine de caractère et retournant un entier.

Une description technique

La description ici faîtes n'est que très partielle et est voué à être détaillé à l'avenir.

Gufo est codé en Ocaml et fait environs 5000 lignes. Il repose beaucoup sur les mécanismes d'ocaml (type (le int de Gufo est strictement le int d'ocaml, gestion de la mémoire…) mais ajoute le mécanisme de commande externe, de set et map sans passer par des foncteurs et rend plus facile (d'avantage "script") son utilisation. Il ne cherche pas atteindre ces mécanismes avancés.

Le code est parsé puis est transformé dans une représentation intermédiaire optimisée (tous les symboles sont remplacés par des entiers ce qui permet des gains importants de performances).

Gufo est un language fonctionnel et si il permettra quelques astuces impérative, le restera.

Image of the core

L'état du projet

Mon objectif était d'avancer au maximum le projet avant de le présenter pour pouvoir donner autant de précision et de garantie que possible sur les qualité du langage. Le projet tout de même apparaît bien ambitieux et je ne peux pas d'avantage le développer sans soutien financier.
J'ai donc décidé de faire cette présentation, pour présenter honnêtement le projet, vous permettre de l'évaluer et de considérer si il vous paraissais utile de l'aider. Votre avis me sera utile pour savoir si le projet semble avoir du sens.

Votre aide, cela peut être des remarques, des conseils, et également des perspectives de financement si le projet vous semble pertinent. Selon les réactions, j'organiserais une campagne de financement.

Je tiens à préciser que quoi qu'il arrive, le logiciel est destiné, si il est publié à être publié sous licence libre. Pour autant, je considère que tout travail (utile) mérite salaire: la qualité de la publication dépendra de l'aide reçu.

Pour l'instant, je souhaite en être le seul développeur, jusqu'à cette première publication éventuelle. Ensuite, nous verrons la situation, mais comme indiqué, le projet sera libre.

Voici les nombreux points qui devraient être travaillés pour envisager une version "aboutie":

  • Finalisation du type-checker (déjà relativement avancé)
  • Amélioration de la gestion des commandes (en particulier sur le type permettant de stocker l'exécution d'une commande: que veux t'on stocker?)
  • amélioration des levés d'erreurs, en particulier position de l'erreur dans le code (je considère cela tout à fait essentiel mais ai laissé cette fonctionnalité pour plus tard car elle n'était pas nécessaire pour estimer la faisabilité pure.)
  • Gestion et écriture des modules systèmes (List, Set, Map, Cmd….) fournissant un ensemble de fonctions standards.
  • Documentation complète
  • Mise en place d'outils de programmation impératives: séquence d'expressions, pointeurs sur des valeurs mutables…
  • Validations et écriture de programmes de test conséquents
  • possibilité qu'une structure étende une autre structure (pourrait ressembler à des mécanismes de programmation objet).
  • Pour les questions de gestions de la mémoire, je me repose sur Ocaml, langage dans lequel est codé Gufo.

Si vous êtes curieux vous pouvez me contacter: http://pvittet.com/?page=contact.

Pour conclure, j'ai eu, en tant qu'ingénieur en informatique, plusieurs fois l'opportunité de travailler avec des chercheurs, Je n'aurais certainement pas eu l'imagination ni les moyens de me lancer dans un tel projet sans ces expériences. Aussi, je tiens à leur exprimer mes remerciements.

  • # mmmhhh

    Posté par  . Évalué à 10. Dernière modification le 07 décembre 2017 à 11:10.

    Je tiens à préciser que quoi qu'il arrive, le logiciel est destiné, si il est publié à être publié sous licence libre. Pour autant, je considère que tout travail (utile) mérite salaire: la qualité de la publication dépendra de l'aide reçu.

    Le paradigme habituel du logiciel libre, c'est de publier tôt et souvent ; tu ébauches un projet, tu partages ton code, et une communauté se forme autour du code si le projet est intéressant, pour l'amener à maturité. Pour la plupart des projets, le bénéfice du mainteneur du logiciel est assez faible, à part la reconnaissance de la communauté et le plaisir de disposer du logiciel que tu souhaites. Les bénéfices sont quand même indirects, je pense par exemple que de maintenir un beau logiciel libre reste une ligne très intéressante sur un CV, et qu'éventuellement ça peut aussi permettre d'en vivre plus directement (un logiciel populaire peut attirer des dons).

    J'ai l'impression que ce que tu proposes, c'est un nouveau paradigme qui est apparu avec les campagnes de financement participatif. Est-ce qu'il y a des exemples qui ont vraiment fonctionné? Personnellement, je pourrais envisager de faire un don pontuel à un projet en développement que j'utilise régulièrement, par exemple. Par contre, je me vois mal donner de l'argent à un projet "futurement" libre pour lequel il n'existe qu'un cahier des charges.

    Je comprends bien que le problème de fond, c'est le financement du logiciel libre. Jusqu'ici, les modèles économiques restent fragiles et réservés à des logiciels particuliers (par exemple, ceux avec lesquels ont peut vendre du service). Beaucoup de logiciels libres sont développés bénévolement, ou par des contributeurs salariés. L'idée de "vendre" le développement d'un logiciel libre paraît logique du point de vue du développeur, mais elle est illogique du point de vue de l'utilisateur, à qui tu demandes de payer pour un logiciel qui n'existe pas, alors qu'il a à disposition des logiciels (peut-etre imparfaits) qui existent déja et qui sont disponibles gratuitement.

    Tu peux considérer que tout travail mérite salaire, ça n'en fait pas un mantrâ miraculeux qui va te rapporter des sous. Moi je considère que les professeurs méritent plus de respect, que les SDF devraient avoir un toît, que tous les enfants du monde devraient avoir à manger, etc. La question n'est pas de savoir ce qui devrait être, c'est d'arriver à trouver un système pour arriver à cet objectif. Et là, j'ai juste l'impression que tu ne te donnes pas les moyens d'atteindre cet objectif (ou, pour être plus précis, tu ne donnes pas aux autres les raisons rationnelles pour te permettre d'atteindre cet objectif).

    PS: sur le fond, le projet reste intéressant. À mon avis, plus la compatibilité avec bash sera maintenue, plus la possibilité de faire une transition douce motiverait les utilisateurs à faire le changement.

    • [^] # Re: mmmhhh

      Posté par  . Évalué à 7.

      En fait, je suis d'accord avec toi sur l'essentiel:
      je suis favorable au "publier tôt, publier souvent". Mais n'est ce pas décevoir les premiers utilisateurs potentiels que de leurs fournir un programme qui n'a quasiment jamais été testé (je n'ai testé les premiers programmes que le 30 Novembre)? Il s'agit des deux fonctions que je fourni en exemple.

      je me demande par exemple si le créateur de python (ou d'autres langages) a fournit les sources et a communiqué sur son projet avant d'avoir eu confiance dans le fait qu'il marchait à minima.
      D'aprés la page wikipedia de python, j'en déduis que non:
      d'abord

      its implementation was started in December 1989

      puis:

      In February 1991, van Rossum published the code (labeled version 0.9.0) to alt.sources.[10] Already present at this stage in development were classes with inheritance, exception handling, functions, and the core datatypes of list, dict,

      Ce n'est qu'un exemple, je ne dis pas que python est le projet libre ultime.

      Si je publiais un truc pré-alpha qui plante sur la première commande pipé, est ce que cela valoriserais mon projet? Ou même, sans bug, que le type 'list' existe mais qu'aucune fonction buildin ne permette d'itérer ou de l'utiliser.

      Le but de ce post est aussi de voir si il y a un intérêt potentiel plutôt que de rester travailler dans mon coin, sans consultation de la communauté.J'espère pouvoir offrir un jour à la communauté l'opportunité de tester le projet par elle même.

      Je ne peux pas nier qu'il va falloir que je retrouve un travail (ou du moins de l'argent) et que cela impactera fortement mon temps pour ce projet.

      • [^] # Re: mmmhhh

        Posté par  . Évalué à 1.

        Mom experience personnelle est que on peut recevoir de l'aide sur des bouts de code qui ne marche pas. Par example avec https://github.com/FreeOpcUa/python-opcua , apres un mois ( et peut etre 20 petits commits il y a deja eu un gars qui a envoye un PR alors que le programme ne faisait encore rien d'utils. Donc oui je conseille de publier des les premieres lignes de code.

  • # Rashell

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

    Puisque tu programmes en OCaml je te signale l'existence de Rashell, une bibliothèque qui permet de facilement interagir avec des programmes externes à la manière de ce que le shell propose. Tu peux l l'essayer avec opam: opam install rashell.

    https://github.com/michipili/rashell

    En gros je reconnais 3-4 modes d'interaction typiques avec les programmes externes, comme par exemple l'utilitaire qui nous intéresse pour son effet de bord ou sa sortie:

    (** Execute the given command and return its exit status, the content
        of stdout and of stderr. *)
    val exec_utility : t -> string Lwt.t

    La recherche qui produit ses résultats en mode ligne à ligne:

    (** Execute the given command and return a stream reading the output
        of the command and its exit status with error output. *)
    val exec_query : t -> string Lwt_stream.t

    Le test qui nous intéresse par son code de retour

    (** Execute the given command and test its exit status. An exit status
        of [0] indicates success of the test, [1] indicates failure of the
        test and other values indicate a general error. *)
    val exec_test : t -> bool Lwt.t

    et le filtre, qui permet de passer un flot de lignes à travers in filtre UNIX:

    (** Execute the given command and return a function mapping streams,
        and its exit status with error output. *)
    val exec_filter : t -> string Lwt_stream.t -> string Lwt_stream.t

    Avec ces nouvelles primitives, il est facile d'écrire des interfaces bien typées pour plein de commandes Unix, voir le dossier [src](https://github.com/michipili/rashell/tree/master/src) dans le code du projet. Par exemple Rashell_Posix fournit une interface à quelques utilitaires définis par POSIX (find, test, cp, rm, mv, ln, sed, awk, df and du) des fonctions pour utiliser des dossiers ou des fichiers temporaires, ainsi que des interfaces à quelques fonctions de git ou de docker. Par exemple voici le code de Rashell_Git.topleveldir qui retourne le dossier racine d'un dépôt git:

    let topleveldir ?workdir () =
      exec_utility ~chomp:true
        (command ?workdir ("", [| ac_path_git; "rev-parse"; "--show-toplevel" |]))

    et celui de la commande Rashell_Git.branch_current qui donne le nom de la branche visitée, ou signale une exception lorsque la copie de travail est en “detached HEAD state”:

    let branch_current ?workdir ?env () =
      let argv = [| ac_path_git; "rev-parse"; "--abbrev-ref";  "HEAD" |] in
      exec_utility ~chomp:true (command ?workdir ?env (ac_path_git, argv))
      >>= function
      | "HEAD" -> Printf.ksprintf Lwt.fail_with "Rashell_Git.branch_current: %S: The repository is in detached HEAD state."
                    (match workdir with Some(path) -> path | None -> ".")
      | branch -> Lwt.return branch

    La commande clone de git a de nombreuses options, même en ne tenant compte que d'une fraction d'entre elles on obtient quelque chose de plus touffu:

    let clone ?workdir ?env ?template ?bare ?mirror ?origin ?branch ?(config = []) ?depth ?single_branch ?recursive ?reference ?dissociate ?destdir repository =
      let open Printf in
      let configuration =
        Array.concat(List.map (fun (k,v) -> [| "--config"; sprintf "%s=%s" k v; |]) config)
      in
      let argv =
        Array.concat [
          [| ac_path_git; "clone"; |];
          (option "--template" template);
          (flag "--bare" bare);
          (flag "--mirror" mirror);
          (option "--origin" origin);
          (option "--branch" branch);
          configuration;
          (option "--depth" (Maybe.map string_of_int depth));
          (flag "--single-branch" single_branch);
          (flag "--recursive" recursive);
          (option "--reference" reference);
          (flag "--dissociate" dissociate);
          [| repository |];
          (match destdir with
           | Some(x) -> [| x |]
           | None -> [| |]);
        ]
      in
      exec_utility
        (command ?workdir (ac_path_git,  argv))
      >>= fun _ -> Lwt.return_unit

    Le module Rashell_Docker et les projets https://github.com/michipili/anvil ainsi que https://github.com/michipili/dockertk fournissent d'autres exemples d'utilisation de Rashell.

    Pour en revenir à Gufo c'est un projet certainement intéressant comme véhicule d'apprentissage mais il me semble adopter une approche étrange. Si j'ai à coordonner l'exécution de programmes, il me semble voir deux approches naturelles:

    • Utiliser le shell Bourne Cette solution a deux avantages: Prenièrement la présence d'un shell compatible Bourne est garantie sur presque tous les systèmes Unix, soit ni configuration, ni prérequis; deuxièmement le parallélisme et la concurrence y sont facilement utilisables (pour les tâches simples). Les inconvénients sont aussi bien connus que nombreux: langage rustre, difficile d'accès, pas très adapté à la gestion des erreurs ni au traitement de données… etc.

    • Utiliser un langage de haut niveau Cette solution résout en principe tous les problèmes du shell, mais il y a un problème de déploiement à résoudre, éventuellement le besoin d'écrire une interface agréable pour utiliser les programmes extérieurs , et le besoin d'apprendre la gestion du parallélisme dans le langage en question – pas forcément difficile mais a priori bien plus complexe que le modèle du shell.

    Dans cette grille de lecture, j'ai un peu du mal à voir la place que peut occuper Gufo: si j'ai besoin de configurer la machine sur laquelle je dois effectuer une tâche complexe, autant y installer mon langage préféré et tirer pleinement parti d'un langage puissant et complet.

    • [^] # Re: Rashell

      Posté par  . Évalué à 2.

      Bonjour,

      d'abord merci pour le commentaire pertinent.
      Je ne connaissais pas rashell mais j'avais vu des choses du type shcaml qui ont l'air similaire. Python doit aussi avoir des choses similaire.

      A une époque, je m'étais dis, qu'effectivement créé un simple module avancé était plus facile que de recréer tout un langage. Quand même, pour moi rashell comme les équivalents dans d'autres langages (je sais qu'il y a des pseudo shells python) ne répondent pas à la problématique:

      Faire un "a=$(ls -l | wc -l)" avec rachell doit ressembler à (si je ne me trompe pas):

      let a = exec_filter (command "wc" ["-l"]) (exec_query (command "ls" ["-l"]))

      Il faut importer les bons modules avant
      Au delà d'être lourdingue, cela oblige à bien connaître les fonctions de la doc.
      Le fait d'ajouter des fonctions prédéfinis pour les commandes usuelles ne résout pas le problème: il faut lire la doc et l'on perd le coté universel.

      Avec Gufo, la commande sera:

      let $a = ls -l | wc -l

      Dans un sens dans Gufo, j'ai deux langages qui s'imbriquent: un langage fonctionnel basique qui peut contenir un sous-langage de commande (qui vise a imiter bash). C'est aussi une façon d'aider le nouveau venu: il peut au début utiliser la partie commande et découvrir au fur et à mesure le langage au dessus.

      Je sens qu'a ce moment on va me dire, "oui donc pourquoi c'est mieux que bash", moi, je vais répondre parce que:
      1) la syntaxe est moins pourri (en bash "a=5" est différent de a =5" et différent de "a= 5").
      2) bash ne dispose pas de vrai fonction (je crois que toute les variables sont globales ou des choses comme ça).
      3) je veux avoir des types "riches": list, set, map (je veux aussi avoir quelques différences par rapport à Ocaml (avoir des map et des set sans avoir à définir de foncteurs explicites)
      4) il y a un système pour détecter des erreurs avant exécution du programme:on ne commencera pas a exécuter quoi que ce soit si il existe une variable indéfini.

      Pour le nom (Gufo), je laisse un peu de suspense et donnerais la réponse quand je pourrais faire de nouveaux commentaires.

      • [^] # Re: Rashell

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

        Il faut importer les bons modules avant. Au delà d'être lourdingue, cela oblige à bien connaître les fonctions de la doc.

        Oui bref, c'est un peu longuet et tout par rapport au shell, mais l'avantage c'est qu'on a un vrai langage de programmation (but recherché). Si la concision est vraiment importante on peut se poser la question d'écrire un PPX – ce qui est a priori bien plus simple que d'écrire un langage complet!

        Dans un sens dans Gufo, j'ai deux langages qui s'imbriquent: un langage fonctionnel basique qui peut contenir un sous-langage de commande (qui vise a imiter bash). C'est aussi une façon d'aider le nouveau venu: il peut au début utiliser la partie commande et découvrir au fur et à mesure le langage au dessus.

        Je comprends bien, mais il ne faut pas perdre de vue qu'il y a une longue histoire de remplacements au shell qui sont tous de façon tout à fait défendable mieux que le shell de Bourne (le /bin/sh historique) mais en pratique ils restent d'utilisation relativement marginale et presque uniquement pour l'usage interactif. La seule explication que j'y trouve est le problème du déploiement que j'évoque dans le commentaire auquel tu réponds: les solutions les plus faciles sont soit d'utiliser ce qui est déjà là soit d'utiliser un langage plus complet qu'on connaît bien et qui permet de programmer concisément, comme par exemple OCaml, Common Lisp, Python, Perl, Ruby, TCL, ou autre…

        Je sens qu'a ce moment on va me dire, "oui donc pourquoi c'est mieux que bash", moi, je vais répondre parce que:

        À mon avis ce n'est pas du tout la question qui se pose, je pense que tout le monde sera d'accord pour dire que Gufo est mieux que bash, puisque bash est assez limité comme langage de programmation: encore une fois le problème c'est le choix d'une solution par un programmeur qui veut décrire un traitement sur une machine: si je connais bien Bourne et perl disons, qu'est-ce qui me pousserait à apprendre et installer gufo au lieu de soit utiliser Bourne qui est déjà là soit si mon programme est plus complexe, installer perl et toutes les libraries dont j'ai besoin pour programmer ma solution?

        2) bash ne dispose pas de vrai fonction (je crois que toute les variables sont globales ou des choses comme ça).

        C'est inexact, il y a deux mécanismes qui permettent de limiter la portée des variables: le mot clef local et l'exécution d'un sous-shell.

  • # Fish

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

    Il me semble qu'il existe déjà des projets de shell modernes qui visent à offrir une syntaxe plus saine que Bash. Par exemple https://fishshell.com/.
    Qu'apporte vraiment ton projet par rapport à ça et pourquoi est-ce qu'il réussirait là ou Fish (et d'autres) n'a jamais réussi à percer ?

    • [^] # Re: Fish

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

      Un shell qui n'a pas spécialement percé mais qui a des idées intéressantes c'est PowerShell de Microsoft. Disponible sous Linux aussi je crois.

      Ce qui est intéressant c'est de pouvoir manipuler des objets directement. Cela autorise une certaine encapsulation, la syntaxe peut être plus claire et cela évite que chaque commande ayant une sortie texte ne doit être découpée ou interprétée de manière compliquée à coup de grep, cut, awk et autre pour ne conserver que ce que l'on veut. Ce qui est sensible à la moindre modification de la sortie du programme d'origine.

      Je pense que c'est quelque chose qui a une certaine valeur ajoutée. Et qui n'est pas si compliquée non plus à mettre en œuvre.

      • [^] # Re: Fish

        Posté par  (site web personnel) . Évalué à 7. Dernière modification le 07 décembre 2017 à 16:57.

        Le problème que PowerShell sous Linux, c'est qu'il ne doit pas fournir beaucoup d'interfaces objets avec notre OS, si?

        Après, j'ai envie de dire que si tu te rends compte qu'un script est trop compliqué en bash, il est peut être temps de le refaire en python/ruby/…

        • [^] # Re: Fish

          Posté par  . Évalué à 5.

          Le problème que PowerShell sous Linux, c'est qu'il ne doit pas fournir beaucoup d'interfaces objets avec notre OS, si?

          Le probleme de powershell sous windows c'est qu'il ne fournit pas beaucoup d'interfaces objets non plus.

          Les fans n'arretent pas de praise powershell car il est conceptuellement evolue en comparaison a la CLI sous linux.. mais j'ai du mal a croire qu'ils ont deja essaye d'utiliser ce machin pour de vrai, pour faire autre chose que hello world.

          Sans parler de la documentation quasiment absente.. une fois j'avais voulu scripter quelques interactions bluetooth en powershell sous windows, j'ai laisse tombe. Il n'y avait tout simplement rien de comparable a la documentation et qualite qu'on a sous linux avec bluetoothctl.

          Powershell est un meilleur langage de programmation que bash/zsh/fish.. mais c'est une merde au niveau interactivite (en comparaison a Zsh ou Fish) et c'est une merde au niveau ecosysteme de programmes en ligne de commandes ayant des interactions avec l'OS. C'est aussi de la merde au niveau documentation.

    • [^] # Re: Fish

      Posté par  . Évalué à 2.

      J'avais bien repérer fish (par wikipedia), je n'avais pas vu le beau site web.
      Je viens de le tester un peu mais au minimum, la partie langage de script n'est pas sérieuse:

      function fish_title
      echo $argv[1]
      end

      Il prend un nombre indéfini d'argument et non directement nommées (mis dans le tableau argv).
      Je ne pense pas que l'on puisse avoir des types riches.
      je vois aussi qu'on ne peux pas utiliser les variables comme commandes (ou alors il faut passer par eval.

      Mon projet aurait typage beaucoup plus strict (fonctions ayant un nombre connu et typé d'argument). Mon projet reposerait sur d'avantage de concept des langages fonctionnels.

  • # Pourquoi Gufo ?

    Posté par  . Évalué à 5.

    Merci de ta présentation, néanmoins la toute première question qui me vient est Pourquoi Gufo ? c'est-à-dire :
    - Pourquoi as-tu choisi d'appeler ton shell "Gufo" ?
    - Que veut dire le terme "Gufo" qui pour moi ne signifie absolument rien, si ce n'est Gnu Unidentified Flying Object ?
    - Est-ce un un sigle, un acronyme, une abréviation, un mot-valise ou un troll comme Java ?
    - Pourquoi ne pas avoir choisi un nom se terminant en sh ou shell ? Comme par exemple osh qui ferait à la fois référence au langage Ocaml dans lequel ton shell est codé et au fait que ce soit un shell…

    Il me semble que ce principe s'appelle la programmation par intention et que c'est la première étape à laquelle réfléchir.

    C'est un reproche que l'on peut faire à de nombreux programmes, notamment:
    - mdadm qui signifie Meta Disk ADMinistration, dont la signification est cachée dans les abysses d'Internet et n'apparaît pas sur les principales pages y faisant référence et dont je suppute que la majorité des linuxiens de tous niveaux ne savent pas ce que cela signifie (mais je viens d'y remédier)
    - Ajoutez en commentaire la liste des programmes qui vous viennent à l'esprit…

    • [^] # Re: Pourquoi Gufo ?

      Posté par  . Évalué à 5.

    • [^] # Re: Pourquoi Gufo ?

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

      Ajoutez en commentaire la liste des programmes qui vous viennent à l'esprit…

      Pas la peine, Debian l'a déjà fait: https://wiki.debian.org/WhyTheName

      • [^] # Re: Pourquoi Gufo ?

        Posté par  . Évalué à 2.

        Merci, je mourrais moins bête ce soir, et la prochaine fois que je chercherais le nom d'un programme je regarderai là.

        Il n'y a pas de programme linux "why" ou "whythename" permettant d'obtenir la même chose ?

        Et je remarque que mdadm n'y figure pas !
        Et que pour btrfs ne figure pas la défintion ButterFS qui provient du CoW (Copy on Write)
        Et pour le reste je verrai quand j'aurais le temps…

        Est-ce que quelqu'un peut y remédier ?

    • [^] # Re: Pourquoi Gufo ?

      Posté par  . Évalué à 4.

      • Est-ce un un sigle, un acronyme, une abréviation, un mot-valise ou un troll comme Java ?

      Ce n'est pas un sigle, pas un acronyme, pas une abréviation, même pas un mot-valise… il ne reste donc que le troll.

      J'ai pris pour tradition de donner à mes programmes un nom d'animal en esperanto. J'ai beaucoup hésité a respecter la tradition ou pas. Par ailleurs, je voulais un nom court (4 lettre semble courant pour un language de shell). C'est aussi bien qu'il n'y ai pas 'sh' pour montrer l'écartement par rapport à la syntaxe des shell posix classiques (sh,bash zsh).

      Titre de l'image

  • # Hello

    Posté par  . Évalué à 5.

    Qu'est-ce qui vous déplaît dans le shell classique ?

  • # L'œuf ou la poule

    Posté par  . Évalué à 2.

    Pour autant, je considère que tout travail (utile) mérite salaire: la qualité de la publication dépendra de l'aide reçu.

    Justement, c'est plutôt l'aide reçue qui dépend de la qualité de publication, même si c'est quelque chose de minimal au début. Si tu veux que le projet se développe, je t'encourage à le mettre sous une licence libre et sur un Git public.

  • # Pour être sur

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

    Une simple commande shell est un programme Gufo valide, c'est ce qui lui permettra d'être un language de shell. La commande ls
    permet de lister les fichiers du répertoire courant. Ce programme permettra donc l'affichage de cette liste.

    Quand je lis ça, j'ai comme un doute sur le fait que tu ais compris ce qu'est un Shell… Il est constitué de commandes internes (echo, set, …) et de commandes externes(echo, ls, mkdir, …). Si tu le savais, alors ta phrase n'est vraiment pas claire :-)

    Sinon, tu as du remarquer le piège, il y'a deux fois echo dans mon exemple, en effet, la commande externe echo n'est jamais utilisée quand on fait du bash (à moins de l'appeler directement) et c'est la commande interne qui est utilisée.
    J'imagine que la commande externe est là pour des raisons de compatibilité avec les shell utilisant print. Quelqu'un, j'ai bon?

    • [^] # Re: Pour être sur

      Posté par  . Évalué à 2.

      Je reconnais que je croyais que "echo" était uniquement une commande externe. Je ne connais pas toute les arcanes de bash (cela ne m'intéresse pas beaucoup).

      Quand je dis "Une simple commande shell est un programme Gufo valide", tu as raison, je mens (volontairement, pour être plus compréhensible) ou alors je laisse peser beaucoup sur le terme "simple": Gufo ne reproduira l'ensemble des commandes internes de bash (qui peuvent être d'ailleur considéré comme des mots clefs du langage). Gufo dispose de ces propres mots clefs (if, let, fun…). Il n'y aura aucun eval ou exec par exemple(j'assume qu'il n'y aura pas d'évaluation dynamique).

      Je crois que la norme posix définit très précisément ce que doit être un shell posix, avec une syntaxe précise (en gros, celle de bash ou zsh). Mon langage ne cherche pas à en être un. C'est donc aussi bien que je n'utilise pas les lettres "sh" dans le nom du projet.

      Je pense que d'autres langage dit "de shell" possède des mots clefs différents de bash (ou sh). Ce sont simplement des shell non posix.

      Si quelqu'un répond à ta question sur la commande "echo" (je l'aurais plutôt vu comme "y a t'il besoin d'une commande echo interne?"), j'en serais ravi.

      • [^] # Re: Pour être sur

        Posté par  . Évalué à 2.

        connaître son ennemie :)

        $ type echo
        echo is a shell builtin
        $ type /bin/echo
        /bin/echo is /bin/echo
        $ type cat
        cat is /bin/cat
        $ type foo
        bash: type: foo: not found
        $ type if
        if is a shell keyword

    • [^] # Re: Pour être sur

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

      J'imagine que la commande externe est là pour des raisons de compatibilité avec les shell utilisant print. Quelqu'un, j'ai bon?

      Je dirais que ça s'est plutôt passé dans l'autre sens. En principe, toutes les commandes devraient être externes. Mais ce n'est pas possible pour celles qui modifient l'état interne du shell: set, cd, …

      Du coup, ces commandes sont implémentées sous forme de commandes internes (qui peuvent donc accéder et modifier l'état interne du shell: variables, répertoire courant, …).

      Cependant, exécuter une commande externe, c'est lent: il faut faire un fork puis un exec, la commande doit ensuite parser ses arguments, faire son travail, puis se terminer, et enfin le shell peut reprendre la main sur stdin et stdout.

      Du coup, certains shells comme bash se sont permis d'implémenter certaines commandes directement en interne pour qu'elles soient plus rapides. En créant au passage des incompatibilités parce que l'une ou l'autre version n'accepte pas forcément exactement les mêmes arguments…

  • # Quitte à faire un nouveau shell…

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

    J'ai l'impression que l'apport de Gufo se situe essentiellement au niveau de la syntaxe.
    Il y a certainement beaucoup de progrès à faire, mais n'y a-t-il pas d'autres axes d'amélioration ?

    Quelques idées en vrac :

    • doit-on garder toujours uniquement stdin, stdout et stderr ? Pourquoi pas avoir plusieurs entrées (exemple tout bête mysql < monfichier.sql << mon-mot-de-passe) et davantage de moyens de sortie ?
    • comme déjà dit plus haut, PowerShell (avec sa manipulation d'objets) permet de se passer de awk/sed/… qui ne sont pas très tolérants aux erreurs, est-ce que cela a été envisagé ?
    • pourquoi ne pas intégrer un véritable affichage graphique (un ls affichant des icônes, au hasard, ou des tooltips qui s'affichent dynamiquement au-dessus de chaque argument ou commande) ?
    • comment améliorer l'intégration de la souris dans l'écriture de commandes ?
    • comment améliorer l'intégration des programmes externes (bash-completion est plus un hack qu'autre chose) ? on pourrait imaginer qu'un programme externe amène sa propre autocomplétion, une aide plus intelligente, une vérification de type avec les commandes suivantes, …
    • pourquoi ne pas tenter quelque chose de totalement différent (par exemple, Automator d'Apple partage beaucoup de points communs avec un shell, pourrait-on envisager quelque chose qui s'inspire des deux ?)

    Bien sûr, il y a potentiellement de gros problèmes de perf à prendre en compte et plein de mauvaises idées là-dedans, et plein d'autres bonnes idées à avoir.

    • [^] # Re: Quitte à faire un nouveau shell…

      Posté par  . Évalué à 0.

      Très bonnes idées.

      Quelques autres idées en vrac:

      • Ajouter des couleurs !
      • Ajouter un formatage des réponses correct (espacement, mise en forme, voire même choisir quelles parties afficher)
      • Permettre de définir ses propres alias facilement, en proposant les commandes ou groupes de commandes les plus utilisées (en conservant et parsant l'historique et faisant des statistiques…)
      • Avoir une interface complète permettant de définir des boutons qui lanceront des commandes ou groupes de commandes
      • Pouvoir choisir la syntaxe du langage shell. Pour ma part je déteste devoir utiliser if … fi ou case … esac. Idéalement pouvoir choisir une syntaxe et un formatage les plus proches possibles d'un autre langage.
      • Pouvoir avoir un retour automatique lorsque tu effectues une action, par exemple lancer automatiquement un ls après avoir fait un cp ou un mv, éventuellement dans une autre fenêtre. Et avoir l'historique des commandes dans une fenêtre à côté, et pouvoir au clavier en sélectionner une pour la rejouer telle quelle, la rejouer en modifiant ses paramètres, etc.
      • Pouvoir avoir des raccourcis claviers facilement configurables…
      • Permettre à l'utilisateur de choisir comment nommer les différentes commandes (parce que grep, awk, sed et cie sont arbitraires et sans aucun sens) ainsi que leurs alias, et en poussant la réflexion plus loin de choisir le type d'interface…
      • Avoir des conventions de nommage complètement cohérentes, comme dans PowerShell. Un très bon article d'introduction ici. Et pouvoir choisir quelles conventions utiliser, parce qu'il y en a forcément avec lesquelles on ne sera pas d'accord / à l'aise.
      • Idéalement l'utilisateur ne devrait avoir à s'exprimer que dans un langage (très proche du) naturel (avec la possibilité d'utiliser des alias pour taper plus vite)
      • Demander d'abord à l'utilisateur ce qu'il veut faire, l'enregistrer, et à partir de là tenter de résoudre son problème en regardant dans une base de donnée publique.

      Bref, un truc user-friendly…

      Toutes ces problématiques me font penser aux technologies du web (HTML 5, CSS 3, Javascript).
      Toute la partie adaptation à l'utilisateur me fait penser aux technologies de machine learning.

  • # Xon.sh

    Posté par  . Évalué à 3.

    Quitte à parler des shells alternatifs, il y a aussi xonsh qui est un shell python avec quelques basheries (pipes, alias et completion). Ça pourrait peut-être t’inspirer sur certains points ?

    • [^] # Re: Xon.sh

      Posté par  . Évalué à 0.

      Pour être honnête, il y a pas mal d'idées qui me semblent proche de celles que je veux utiliser.
      En plus c'est super bien présenté.

      Bon, moi, j'ai l'avantage et l'inconvénient de pas être lié à python.

      Merci pour le lien.

  • # Montre-moi ton Shell typé !

    Posté par  . Évalué à 4.

    Votre avis me sera utile pour savoir si le projet semble avoir du sens.

    “From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds)
    Date: 25 Aug 91 20:57:08 GMT
    […] just a hobby, won’t be big and professional […]”

    Pour le reste, dans l'esprit du commentaire de Michaël, je dirais que l'approche la moins bidouilleuse serait de partir sur un langage de haut niveau, statiquement typé et de le doter d'une interface adéquate par rapport à tes besoins. Ici, le domaine interface est bien défini, c'est le Shell. Je propose alors Haskell comme langage et turtle comme bibliothèque procurant une interface mimant le Shell. Gabriel Gonzalez a écrit un excellent tutoriel sur cette bibliothèque auquel je renvoie au lieu de tenter de présenter la chose ici ; voici plutôt un exemple adapté des cas d'utilisations présentés dans le journal.

    #!/home/foo/.local/bin/stack
    -- stack runghc --package turtle
    
    {-# LANGUAGE OverloadedStrings #-}
    
    import Turtle (
        ( <$> )
        , Alternative ( empty )
        , Applicative ( pure )
        , FilePath
        , MonadIO
        , Text
        , fold
        , input
        , inshell
        , lineToText
        , ls
        , output
        , unsafeTextToLine
        , view
        )
    import Control.Foldl ( head, length )
    import Data.Text ( concat, pack )
    import Prelude ( ( . ), ( >> ), Maybe ( Just, Nothing ), Show ( show ) )
    
    main = eg1
        >> eg2 "nb_files.txt"
        >> eg3 "ls" "ls_opts" "log_ls"
    
    -- ls
    eg1 = ( view . ls ) "."
    
    -- ls -l | wc -l > nb_files.txt
    eg2 outFile = do
        fileCount <- fold ( ls "." ) length
        outFile `output` ( pure . unsafeTextToLine . pack . show ) fileCount
    
    -- Variation sur le thème « exécuter [un] programme externe […] prenant en paramètre une chaine de caractères [contenue dans un fichier …] »
    eg3 :: MonadIO io => Text -> FilePath -> FilePath -> io ()
    eg3 prog optFile logFile = do
        mayOpts <- fold ( lineToText <$> input optFile ) head
        case mayOpts of
            Nothing     -> output logFile ( inshell "Bad options" empty )
            Just opts   -> output logFile ( concat [ prog, " ", opts ] `inshell` empty )

    En rendant hommage au problème de l’œuf et de la poule, testons ce script (nommé code.hs) sous Bash.

    mkdir woe
    pushd woe
    touch ceci cela ça
    printf '%s\n' '-l --human-readable --context' > ls_opts
    chmod u+x code.hs
    ./code.hs
    cat log_ls

    Les sorties :

    FilePath "./code.hs"
    FilePath "./ls_opts"
    FilePath "./\231a"
    FilePath "./cel\224"
    FilePath "./ceci"
    -rw-r-----. 1 foo foo unconfined_u:object_r:user_home_t:s0…

    Bref, cette bibliothèque et le Shell, c'est comme les chéloniens et les carapaces. Cependant, comme sous-entendu au début de ce commentaire, que rien ne t'arrête dans tes expérimentations car, après tout, un OS entier est né par bidouillage.

  • # Comment faire ?

    Posté par  . Évalué à 3.

    Si tu veux faire un super journal, je pense que tu peux expliquer comment on fabrique un nouveau langage.
    Enfin personnellement ça m'intéresse autant voir plus que le nouveau langage en lui même ;)

  • # Un autre jeune shell: Oil

    Posté par  . Évalué à 4.

    Un autre projet de nouveau shell intéressant est Oil Shell, dont le créateur communique beaucoup et très bien (en anglais), par exemple sur son blog. Certaines idées me semblent assez déraisonnables (l'auteur a implémenté son propre système de build et créé un fork de Python pour y écrire son projet), mais c'est rigolo et intéressant à suivre.

  • # Un langage de shell 'fonctionnel'

    Posté par  . Évalué à 5.

    A mon avis, tes utilisateurs potentiels ne vont pas être très nombreux: les utilisateurs avancés des shells qui en ont marre des langages de shell actuels, et bien ils ont quand même leurs habitudes dans un/des langages impératifs (shells, Perl, Python) leur proposer un langage fonctionnel comme remplacement bof..

    Quand je vois ton exemple avancé, tout ces 'in(' ça me pousse plutôt à être 'out'!

    Ca n'est pas la première fois que je note ça, j'ai commencé à lire un livre sur OCaml et bien qu'OCaml soit sensé être un langage "mixte" fonctionnel OU impératif, l'auteur du livre ne s'intéressait qu'à l'aspect fonctionnel: je n'ai pas terminé le livre..

  • # Ammonite-Shell

    Posté par  . Évalué à 2.

    Puisque chacun y va de son alternative…

    Où est-ce que Gufo se situe par rapport à Ammonite-Shell (un shell UNIX basé sur le REPL Ammonite pour Scala) ?

    Ce shell, c'est en gros une REPL Scala avec quelques fonctions et facilités (DSL) pour interagir avec le système (notamment exécuter des commandes externes). Donc on y retrouve toutes les spécificités de Scala : langage objet et fonctionnel, sûreté de typage, etc.

    L'inconvénient (par rapport à bash), c'est que du coup, c'est plutôt verbeux pour faire les choses simples.

Suivre le flux des commentaires

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