Journal Rashell – Une bibliothèque pour remplacer les scripts shells par du Lisp

Posté par  . Licence CC By‑SA.
Étiquettes :
8
11
oct.
2020

Rashell est une bibliothèque Common Lisp pour écrire des remplacements robustes et propices à la maintenance aux scripts shell.

Rashell identifie quelques façons typiques d'interagir avec les programmes externes:

  • les utilities qu'on utilise comme cp, rm, mv;
  • les tests qu'on utilise comme des prédicats, notamment test ou parfois grep.
  • les queries qu'on utilise pour produire une liste de résultats, comme find ou grep par exemple.
  • les filters qu'on applique sur un fichier, une séquence de lignes, une string, etc; par exemple sed et awk.

Voici des exemples de Rashell en action:

RASHELL> (run-utility (cp #p"LICENSE" #p"LICENSE.bak"))
""
""

RASHELL> (describe (cp #p"LICENSE" #p"LICENSE.bak"))
#<COMMAND #P"/bin/cp" :PENDING {10023C20A3}>
  [standard-object]

A command to run the program #P"/bin/cp" on the arguments ("LICENSE" "LICENSE.bak").

Status:
  The command has not been started yet.

RASHELL> (run-query (find* '(:and (:has-kind :regular)(:name "*.lisp")) #p"src"))
("src/package.lisp" "src/posix.lisp" "src/util.lisp" "src/rashell.lisp")

RASHELL> (do-query (regular-file (find* '(:and (:has-kind :regular)(:name "*.lisp")) #p"src"))
           (format t "~&Regular file ~A" regular-file))
Regular file src/package.lisp
Regular file src/posix.lisp
Regular file src/util.lisp
Regular file src/rashell.lisp
NIL

RASHELL> (run-query (df #p"."))
(#S(FREE-DISK-SPACE
    :DEVICE "/dev/disk1s6"
    :BLOCKS 976797816
    :USED 255096096
    :FREE 489110320
    :CAPACITY 35.0
    :MOUNTED-ON "/System/Volumes/Data"))

RASHELL> (run-filter (sed "/TODO:/p" :echo t) "(define-command sed (…)
# TODO: Prepare SED-program language DSL instead of using a string SED-program.
…")
"# TODO: Prepare SED-program language DSL instead of using a string SED-program."

Pour l'instant Rashell est seulement compatible avec SBCL.

  • # Snif !

    Posté par  . Évalué à 3.

    Salut,

    Pas une trace de lush à priori :(

    Bonne chance !

    Matricule 23415

    • [^] # Re: Snif !

      Posté par  . Évalué à 2.

      Qu'est-ce que c'est lush? Il me semble avoir compris en survolant la page qu'il s'agissait d'un langage “à la Lisp” mais qui ne cherche pas du tout à implémenter un Lisp standard (par exemple, pas trace de CLOS). En tout cas merci du lien, je m'intéresse aussi au calcul scientifique et à l'apprentissage automatique, j'y consacrerai un peu de temps.

      Comment as-tu découvert lush et pour quoi t'en sers tu?

      • [^] # Re: Snif !

        Posté par  . Évalué à 3.

        Salut,

        Oui, c'est un langage "à la Lisp".

        Je ne me souviens plus spécialement comment je l'ai découvert. A l'époque j'étais à la recherche de langages pouvant faire du machine learning.

        Cependant, je ne l'utilise plus du tout, et j'ai bien peur au vu des actualités que le projet soit à l'arrêt (pas vérifié).

        C'était juste un petit moment nostalgie ;)

        Matricule 23415

  • # Rashell

    Posté par  . Évalué à 2.

    Si j'ai bien compris c'est un shell lisp qui a des helpers pour les utilitaires externes.

    Mais pour ça :

    (do-query (regular-file (find* '(:and (:has-kind :regular)(:name "*.lisp")) #p"src"))

    C'est forcément une implémentation spécifique, non ?

    https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

    • [^] # Re: Rashell

      Posté par  . Évalué à 2.

      Ce n'est pas vraiment un “shell lisp” ce que je montre en exemple c'est juste un usage interactif dans la boucle d'interaction mais les fonctions en question sont “juste” des fonctions utilisables dans n'importe quel programme. (Lisp gomme beaucoup de différences entre l'usage interactif du langage et l'usage non interactif.)

      La bibliothèque définit un type “command” et des fonctions pour exécuter une commande selon les modes que j'ai décrits “utilitaire”, ”test”, ”recherche” ou “filtre”. L'utilisation de la fonction “cp” renvoie donc une commande, et pour rendre l'écriture d'interfaces comme la fonction “cp” et le programme Unix “cp” facile, j'ai écrit une macro qui s'occupe de toute la plomberie et qui s'utilise comme ça:

      (define-command cp (pathname-list destination)
        ((follow :flag "-H")
         (force :flag "-f")
         (recursive :flag "-R"))
        (:program #p"/bin/cp"
         :documentation "Run cp(1) on PATHNAME-LIST and DESTINATION."
         :reference "http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html"
         :rest (append (ensure-list pathname-list) (list destination))))

      En évaluant ce bout de code, Lisp définit une fonction “cp” qui comprend les options “follow”, ”force” et “recursive” et les traduits en les arguments correspondants du programme “cp”.

      Cette macro traite “le cas facile” mais pour “find*” j'ai du comme tu le devinais dans ton commentaire écrire une interface spécifique:

      (defun find* (predicate-expr pathname &key directory environment follow)
        "Prepare a find(1) command on PATHNAME with the given PREDICATE-EXPR.
      The options are
       FOLLOW
         If set, symbolic links are followed.
       DIRECTORY
          A pathname to a working doirectory to use when running the command.
      "
        ;; References: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html
        (make-instance 'command
                       :program #p"/usr/bin/find"
                       :directory directory
                       :environment environment
                       :argv (append
                              (when follow (list "-L"))
                              (mapcar #'define-command/to-string (ensure-list pathname))
                              (find-predicate-to-argv predicate-expr))))

      (Le “make-instance” en Lisp c'est comme le “new” en C++ pour faire vite.)

      La partie compliquée est cachée dans la fonction “find-predicate-to-argv” qui convertit un prédicat comme '(:and (:has-kind :regular)(:name "*.lisp")) en quelque chose que le programme “find” comprend.

    • [^] # Re: Rashell

      Posté par  . Évalué à 3.

      un shell lisp

      pour ça, voir shcl https://github.com/bradleyjensen/shcl (un shell posix & Lisp mélangés), ou clesh (https://github.com/Neronus/clesh/), une interface encore plus mince entre lisp et le shell.

  • # Une bibliothèque qui rosse du poney !

    Posté par  . Évalué à 4.

    N'attendez pas la chandeleur pour l'installer.

  • # Clojure babashka

    Posté par  . Évalué à 1.

    Sinon, en Clojure au lieu de Common Lisp, il y a babashka.
    Comme toujours, très bien pour un usage perso, mais impossible à faire adopter au boulot sur un projet en équipe. Pas assez "mainstream" comme on dit.

Suivre le flux des commentaires

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