Présentation d'Easy Shell, un shell original dérivé de Lisp

Installation

Téléchargement des sources

Les sources ainsi qu'une documentation complète sont disponibles sur le Web (cf encadré).

Compilation

Décompressez l'archive :

$tar -xvzf esh-0.8.tar.gz

Déplacez-vous dans le répertoire contenant les sources :

$cd esh

Editez le fichier Makefile, supprimez les # en tête des lignes :

#INC=-Ifake-readline/

#LIB=fake-readline/readline.c

Et commentez (#) les lignes :

INC=-I/usr/include/readline

LIB=-lreadline

Vous pouvez désormais compiler Esh :

$make

Et copier l'exécutable dans /bin, propriétaire root, droits d'exécution rwxr-xr-x :

$su root

$cp esh /bin

$chmod 755 /bin/esh

Shell par défaut

Si vous souhaitez ouvrir vos sessions avec Esh, modifiez la ligne de /etc/passwd associée à votre nom d'utilisateur. Par exemple :

user:fqf1je4Dd2I7g:500:500::/home/user:/bin/esh

Listes

Les listes (l) sont formées d'atomes et d'autres listes (cf série consacrée à Scheme).

Atomes

On distingue constantes et identificateurs.

Types

Nous retrouvons les types classiques. Entier (i),

booléen (b), chaîne de caractères (s), descripteur de fichier (f), quelconque (a).

Exemple : -1, 0, 1

false, true

easy shell

standard, stderr

Primitives

Commandes système

En mode interactif, Esh affiche $ puis attend l'entrée d'une commande externe :

Exemple : $ls

GNUstep mbox ...

ou d'une commande interne tapée entre parenthèses :

(cd [<s>]) Change le répertoire actif

(exit [<i>]) Quitte Esh et retourne le code i

Exemple : $(cd /)

$(exit 1)

Variables d'environnement

Esh hérite de l'environnement de son processus père :

(get <s>) Retourne la valeur de s

Exemple : $(get HOME)

=>/root

et en autorise la modification :

(set <s> <s'>) Change la valeur de s

Exemple : $(set HOME /root)

Calcul

Il se limite aux entiers :

(+ <i> ...) Retourne i + ...

(- <i> ...) Retourne i - ...

(* <i> ...) Retourne i * ...

(/ <i> ...) Retourne i / (* ...)

Exemple : $(+ -1 0 1)

=>0

$(/ 10 5 2)

=>1

Chaînes

Les chaînes peuvent être scindées ou regroupées :

(chars <s>) Retourne les caractères de s

(squish <s> ...) Retourne la concaténation de s ...

Exemple : $(chars esh)

=>e s h

$(squish e s h)

=>esh

Listes

Les listes permettent la représentation de données complexes :

(car <l>) Retourne le premier élément de l

(cdr <l>) Retourne les éléments suivants de l

(list ...) Retourne une liste constituée des éléments ...

(unlist <l>) Retourne les éléments de l

Ex. $(car ~(a b c))

=>a

$(cdr ~(a b c))

=>b c

$(list a b c)

=>(a b c)

$(unlist ~(a b c))

=>a b c

Entrées sorties simples

Elles concernent l'accès à la console :

(print ...) Affiche ...

(nl) Saute une ligne

(read <s>) Lit et retourne une chaîne

Ex. $(print Hello World!)

Hello World!

$(read >)

Fichiers

Nous disposons des descripteurs prédéfinis :

(standard) canaux standards

(stderr) canaux standards d'erreur

Nous pouvons accéder aux fichiers :

(file-open <s> <s'>) Ouvre un fichier s' de type s, retourne

le descripteur associé ou () si erreur

File Mode normal

Append Mode ajout

...

(file-read <f>) Retourne le contenu de f

(file-write <f> <s>) Ecrit s dans f

et en contrôler le type :

(file-type <s>) Retourne une chaîne décrivant le fichier nommé

s s'il existe, false sinon.

Regular Fichier régulier

Directory Répertoire

Link Lien symbolique

...

Ex. $(file-type /etc/issue)

=>regular

$(file-read (file-open file /etc/issue))

=>Red Hat Linux release 5.2 (Apollo)

Programmes

Un programme est une liste lue par l'interprète et soumise à évaluation. Au lancement, Esh lit les fichiers de configuration /etc/eshrc et ~/.eshrc s'ils existent. Vous pouvez y définir de nouvelles commandes:

(define <s> ...) Définit une commande s

Ex. (define hello

~(print Hello World!))

$(hello)

Hello World!

Ici, l'interprète détermine l'association hello <-> (print Hello World) puis lance l'évaluation du terme droit. Aucun marquage ne distingue une fonction d'une liste. D'où la nécessité de quoter les arguments pour qu'ils ne soient pas évalués.

Quotes

Une liste marquée ~ n'est jamais évaluée. Dans l'exemple précédent, nous souhaitions associer à hello la liste (print Hello World!) et non Hello World!, résultat de son évaluation.

Arguments

Esh les stocke dans une pile de données :

(push <a>) Empile a

(pop) Dépile et retourne le sommet de la pile

(top) Retourne une copie du sommet de la pile

(rot) Permute sommet et élément suivant de la pile

Retourne une copie du nouveau sommet

(stack) Retourne une copie de la pile

Exemple : (define arg

~(stack))

$(arg a b c)

=> a b c

Structures de contrôle

Séquence

Un programme simple exécute une suite d'instructions :

(begin ...) Exécution de ...

Sélection

Une sélection opère un aiguillage :

(if <a> <a'> <a''>) Evaluer a' si a = true, a'' sinon

en conjonction avec les prédicats suivants :

(> <i> <i'>) Retourne true si i > i'

(< <i> <i'>) Retourne true si i < i'

(= <s> <s'>) Retourne true si s = s'

(null? <a>) Retourne true si a = ()

(not <b>) Retourne true si b = false

(or ...) Retourne false si tous les arguments valent false

(and ...) Retourne true si tous les arguments valent true

Note : Par défaut, est considérée true toute valeur différente de false.

Récursion

La récursion est une manière élégante de décrire un problème en fonction d'un problème de même nature mais de moindre complexité. Prenons l'exemple de la fonction factorielle :

fac(0) = 1

fac(n) = n * fac(n-1), n > 0

soit, après traduction :

(define fac

~(if ~(> (top) 0)

~(* (top) (fac (- (top) 1)))

1))

$(fac 5)

=> 120

(top) représentant l'ordre de la factorielle calculée (fig. 1). L'écriture peut surprendre... mais on finit par s'y habituer ;-)

fig.1 - Récursion

Itération

Formellement, l'itération peut être envisagée comme variante de la récursion. Considérons une fonction traitant les éléments d'une liste :

(define apply

~(if ~(not (null? (rot)))

~(begin

((rot)(rot))

(apply (cdr (list (stack)))))

()))

$(apply chars easy shell)

=>e a s y s h e l l

Notez la permutation des deux arguments au sommet de la pile (rot). L'espace de travail occupé étant proportionnel au nombre d'appels imbriqués, on emploie parfois la structure :

(while <l> <l'> ...) Evalue l' tant que l = true

... valeur initiale de la pile

Notions avancées

Tubes

Il est possible d'enchaîner les commandes :

(run <b> <f> <f'>...) Execute ... et retourne le code résultat

b = true pour un processus détaché

f et f' canaux d'entrée et de sortie

Ex. $(run (false) (standard) (standard) ~(grep kernel

/var/log/messages) ~(sort))

=>Jul 12 07:15:01 localhost kernel: CSLIP ...

et de rediriger les entrées/sorties :

Ex. $(run (false) (standard) (file-open file /tmp/kernel-log)

~(grep kernel /var/log/messages) ~(sort))

Notons l'existence d'une syntaxe abrégée (run-simple ...) correspondant à (run (false) (standard) (standard) ...).

Exemple récapitulatif

Programmons une fonction loop exécutant une commande

... toutes les i secondes.

(loop <i> ...)

Nous créons une fonction pause invoquant la commande externe sleep :

(define pause

~(run-simple (list sleep (top))))

Et l'intégrons :

(define loop

~(begin

(print Loop:)

(while ~(true)

~(begin

(print (nl))

(run-simple (list(cdr(list(stack)))))

(pause (top)))

(stack))))

Ex. (loop 5 free)

=> Loop :

total used ...

total used ...

Conclusion

Esh offre une alternative séduisante aux adeptes de shells classiques. Sa faible utilisation des ressources système (à peine 400 ko en exécution) le destine particulièrement aux petites configurations.

 

Cyril Nocton <cyril.nocton@bigfoot.com>

Relecture Danièle Momont <danimom@club-internet.fr>


© Copyright 2000 Diamond Editions/Linux magazine France. - Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".