Critique du Shell

par Éric Marsden

Les shell Unix (et dans une moindre mesure Perl) souffrent de leur représentation des données en tant que chaines de texte. Ce niveau "plus petit dénominateur commun" d'interface entre modules executables découle naturellement du modèle Unix processus + pipes. Or cette représentation par chaines donne des langages à la fois inefficaces et peu expressifs. Les programmeurs shell sont contraints de parser et reparser des données, le plus souvent avec des outils peu puissants.

Par exemple, pour déterminer le nombre de fichiers qui résident dans un répertoire, un programmeur shell emploie typiquement une expression de la forme ls | wc -l. Sans se préoccuper de savoir s'il est tres efficace de créer deux processus et un pipe pour cette simple opération, on peut remarquer que cet idiom est fausse: les noms de fichier Unix peuvent contenir des retour chariot. En Scsh par contre, le programmeur jouit d'un accès direct aux appels système et d'un ensemble bien plus riche de structures de données. La solution en scsh est (length (directory-files)).

Prenons un autre exemple consistant a déterminer si un fichier donné est setuid. Le pauvre programmeur shell doit faire un grep dans la sortie de ls -l pour chercher le caractère s à la bonne position. Scsh permet un accès direct à l'appel système stat().

La programmation Scsh compare aussi favorablement à la programmation en langage C (mis à part les questions de rapidité et occupation mémoire). Dès qu'une erreur survient, une exception est levée, que le programmeur peut traiter. A comparer avec toutes les vérifications de code retour que doit (devraient!) faire un programmeur C (et ne parlons pas de la programmation multi-thread et la variable globale errno). La gestion automatique de la mémoire et le type de donnée chaine de Scsh éliminent toute une catégorie de problèmes que doit gérer le programmeur C.

Quelques exemples en Scsh

;; Supprimer chaque fichier dans REP contenant la chaine "/bin/tclsh"
(with-cwd rep
  (for-each (lambda (file)
               (if (zero? (run (grep -s /bin/tclsh ,file)))
                     (delete-file file)))
            (directory-files)))
					      
					      
;; Supprimer tous les fichiers C du repertoire courant            
(for-each delete-file (glob "*.c")


;; M'envoyer les logs Apache par email.
(| (cat /var/log/apache/error.log)
   (gzip - --best)
   (uuencode -m /dev/stdout)
   (mail -s "Savage error logs" emarsden@mail.dotcom.fr))

Je termine par une citation de la personne à l'origine de Scsh (Olin Shivers, alors à MIT):

Shell programming terrifies me. There is something about writing a simple shell script that is just much, much more unpleasant than writing a simple C program, or a simple Common Lisp program, or a simple Mips assembler program. Is it trying to remember what the rules are for all the different quotes? Is it having to look up the multi-phased interaction between filename expansion, shell variables, quotation, backslashes and alias expansion? Maybe it's having to subsequently look up which of the twenty or thirty flags I need for my grep, sed, and awk invocations. Maybe it just gets on my nerves that I have to run two complete programs simply to count the number of files in a directory (ls | wc -l), which seems like several orders of magnitude more cycles than was really needed.
(238 kB)
Image grâce à Phillip Greenspun.