Bonjour,
Je vais vous parler d'un sujet qui m'intéresse, le shell. A mon avis le shell actuel qu'on a sur nos machines (souvent bash) a deux gros problèmes, et j'aimerais bien ouvrir une discussion là dessus à l'occasion d'un développement récent que j'ai fait.
1. L'expansion des variables
Tout d'abord, le premier problème du shell sh ou compatible est à mon avis, outre sa syntaxe parfois bizarre, le problème de l'expansion des variables. Pour justifier mes griefs sur la syntaxe, je dirais simplement que je ne vois pas pourquoi les routines sont délimitées par des { } et pas les constructions if/for/case, pourquoi le if se termine par fi, le case par esac et le for/while par done et non pas rof/elihw.
penchons-nous sur le problème de l'expansion des variables. Le problème c'est que le shell découpe les mots après l'expansion des variables, c'est à dire que :
var="mot1 mot2"
cmd a b${var}c d
est équivalent à :
cmd a bmot1 mot2c d
et non pas :
cmd a "bmot1 mot2c" d
Alors que dans le code source on ne voit que 3 arguments passés à la commande cmd (a, b${var}c, d), il y en a en fait réellement 4 (a, b$mot1, mot2c, d).
Ce comportement peut être désirable dans certains cas, mais parfois vraiment malvenu. Par exemple lorsqu'on manipule des noms de fichiers contenant des espaces. En gros cela impose lorsqu'on ne sait pas précisément ce qu'il y a dans la variable à toujours l'entourer de quotes, c'est à dire écrire "$var" au lieu de $var.
Non seulement les débutants qui commencent ne comprennent pas toujours cette subtilité et oublient les quotes, mais bien souvent aussi, des gens expérimentés les oublient en les pensant inutiles.
Un exemple concret. la distribution ArchLinux utilise des script bash pour compiler les logiciels et créer des paquets, on les appelle PKGBUILD (un peu similaires aux ebuilds de gentoo). L'exemple le plus courrant et qui sert de modèle à tous les PKGBUILD est¹:
build() {
cd $startdir/src/$pkgname-$pkgver
./configure --prefix=/usr
make || return 1
make prefix=$startdir/pkg/usr install
}
Où $startdir est le dossier dans lequel les sources se trouvent, et où les fichiers sont installés.
Ici on peut voir que $startdir n'est jamais entre quotes, c'est à dire qu'il est impossible de compiler un package si le chemin courrant contient des espaces !!! En effet, la première commande (cd) échoue déjà car au moins deux arguments lui sont passés en paramètres.
C'est ma première critique concernant la robustesse des script shell.
2. La lenteur
Bash, c'est lent. On le voit avec les script d'init, et je l'ai remarqué plus récamment en remplaçant mon script monolithique ~/.xsession par de nombreux scripts modulaires qui me permet à chaque démarrage de:
- me demander mon mot de passe ssh
- lancer quelques programmes comme xbindkeys, mpd
- demander quelle session lancer (E17, GNOME, KDE, Terminal ...)
Et je constate facilement que c'est lent.
Je sais bien que si on veut faire quelque chose de rapide, on utilise plus quelque chose comme python que bash, mais le shell a tout de même l'avantage de pouvoir facilement lancer des applications tierces facilement, éventuellement de manière asynchrone, en modifiant l'environnement, et en redirigeant les entrées/sorties. Si on veut faire la même chose avec un langage classique, je pense que c'est d'un coup moins facile (si on omet la fonction system() qui utilise d'ailleurs le shell).
Sans compter des projets complexe entèrement programmés en bash. Par exemple le gestionnaire de paquets de Nasgaïa est comme ça (ca a peut être changé depuis). Et ne serait-ce qu'afficher l'aide prend un temps fou.
je me demande alors dans quelle mesure (et je vous pose la question) il ne serait pas intéressant d'avoir un interpréteur shell (type sh ou autre) constamment chargé en mémoire qui pourrait rapidement exécuter n'importe quel script. Bien entandu une attention particulière doit être portée à la sécurité.
3. mon code
je vois ai dit que j'avais codé quelque chose, mais quoi ? J'ai commencé un shell basé sur le langage jam². Jam est un remplaçant à make(1) et possède un langage de script qui a les facultés suivantes :
- les variables ne contiennent pas une seule valeur, mais des listes de chaînes de caractères.
- lors de l'expansion des variables est le produit de tous les termes. Pour illustrer cela, je prend juste l'exemple donné dans la documentation³:
$(X) -> a b c
t$(X) -> ta tb tc
$(X).txt -> az bz cz
$(X)-$(X) -> a-a a-b a-c b-a b-b b-c c-a c-b c-c
- la ponctuation n'est pas reconnue si elle n'est pas entourée d'espaces. Cela permet d'avoir de la ponctuation sans problèmes à l'intérieur des mots.
Ce que j'ai fait à partir de ça, c'est relativement simple. J'ai supprimé toute la partie qui s'occupait de la compilation, de la gestion des dépendances entre les cibles, pour ne garder que le langage Jam et faire en sourte que si une fonction n'est pas reconnue de manière interne par Jam, le programme correspondant dans le $PATH soit exécuté. Et cela n'a pris que quelques heures.
Mon code est disponnible sur launchpad⁴ et si cela vous intéresse, plus de détails dans mon e-mais sur la mailing list de jam⁵.
Des remarques ?
¹ http://wiki.archlinux.org/index.php/ABS_-_The_Arch_Build_Sys(...)
² http://freetype.sourceforge.net/jam/index.html
³ http://public.perforce.com/public/jam/src/Jam.html
⁴ https://code.launchpad.net/~mildred/+junk/jam-shell
⁵ http://maillist.perforce.com/pipermail/jamming/2007-November(...)
# iPython
Posté par GeneralZod . Évalué à 3.
http://ipython.scipy.org/moin/About
Ce n'est qu'un retour aux origines, Python était destiné à servir de shell pour Amoeba.
# Y a pas que Bash dans la vie
Posté par ethtezahl . Évalué à 4.
[^] # Re: Y a pas que Bash dans la vie
Posté par GeneralZod . Évalué à 10.
[^] # Re: Y a pas que Bash dans la vie
Posté par Mildred (site web personnel) . Évalué à 1.
[^] # Re: Y a pas que Bash dans la vie
Posté par tripa . Évalué à 10.
[^] # Re: Y a pas que Bash dans la vie
Posté par Bapt (site web personnel) . Évalué à 6.
Car zsh permet autrement plus de choses que bash notamment en ce qui concerne la programmation. Une bonne lecture (longue) de http://zsh.dotsrc.org/Doc/ pourra te donner un bon apperçu.
La lenteur du shell est relative, comme indiqué plus bas on passe souvent par des appels à des programmes externes (grep awk sed cat etc.) là où ce n'est absolument pas nécessaire en tout en zsh pour ce que je connais. Pour convertir souvent des script bash/ksh en zsh je peux te garantir que quand tu vires which/grep et consors tu as souvent un gain de performances important.
d'ailleurs dans la foulée : dans la version de dev de zsh, un nouveau module est en train de faire son apparition : zcurses => exit dialog \0/.
[^] # Re: Y a pas que Bash dans la vie
Posté par Dabowl_92 . Évalué à 1.
J'ai un collègue qui a fait de très belles choses en jouant avec les séquences terminfo...
En plus c'est portable, ce qui n'est pas forcément le cas de dialog et encore moins du module nzcurses...
[^] # Re: Y a pas que Bash dans la vie
Posté par z a . Évalué à 2.
[^] # Re: Y a pas que Bash dans la vie
Posté par totof2000 . Évalué à 4.
[^] # Re: Y a pas que Bash dans la vie
Posté par Dabowl_92 . Évalué à 5.
Quand j'ai débuté la programmation shell j'enchainais beaucoup les |, et c'est vrai que les performances étaient pas au rendez-vous.
Utiliser correctement le shell, c'est surtout limiter le nombre de process et utiliser toutes ls fonctionnalités offertes par ce dernier.
Jusqu'au jour où finit par utiliser awk parce que le shell est trop pauvre, ensuite on découvre perl, et on fait des scripts perl standalone.
Aujourd'hui quand je veux faire un truc un minimum compliqué, je ne pense même pas à le faire en shell, c'est trop inmaintenable, mais bon y en a qui aiment se faire chier alors bon...
[^] # Re: Y a pas que Bash dans la vie
Posté par tfing . Évalué à 1.
C'est moi où il y a beaucoup d'ironie dans cette phrase ? :)
[^] # Re: Y a pas que Bash dans la vie
Posté par kowalsky . Évalué à 2.
J'ai ecris récemment un soft de 10 000 lignes environs de Perl, et c'est
tres maintenable et structuré.
Tout langage, mal ecris, donne du code difficilement maintenable...
[^] # Re: Y a pas que Bash dans la vie
Posté par tripa . Évalué à 2.
Tu peux avoir l'opinion que tu veux dans la guéguerre (stupide) perl/python/ruby (ou une autre qui m'échappe), tu ne trouveras malgré tout pas grand monde pour prétendre qu'un gros projet bash est plus maintenable qu'un gros projet Perl.
[^] # Re: Y a pas que Bash dans la vie
Posté par Fabien Engels . Évalué à 1.
[^] # Re: Y a pas que Bash dans la vie
Posté par totof2000 . Évalué à 3.
Le shell, c'est bien lorsque tu te retrouve dans un environnement ou tu n'es pas sur d'avoir Perl à disposition partout (anciennes installations d'Unix proprios, NetBSD qui ne dispose pas de Perl dans une installation de base ou certaines boites qui interdisent l'utilisation de ce langage), ou quand tu développe des scripts de démarage par exemple.
Puis il ya des cas ou il est plus simple de s'en sortir avec un awk bien senti qu'avec Perl (cela dit je suis un peu rouillé en Perl, faudrait que je m'y remette).
[^] # Re: Y a pas que Bash dans la vie
Posté par petit_bibi . Évalué à 0.
de savoir si (une ligne, un mot, un process) est présent:
C'est nettement plus rapide que:
Mais c'est dangereux si le fichier est gros.
Pour traiter un fichier ligne par ligne:
C'est plus rapide que:
Il y a pas mal de bidouilles pour gagner en perfs, par exemple avec
les trucs décrits dans la section 'Parameter Expansion' on peut couvrir
pas mal de cas en ce qui concerne le traitement de chaines de caractères.
[^] # Re: Y a pas que Bash dans la vie
Posté par totof2000 . Évalué à 4.
Imagine que dans ton traitement tu fais appel a des grep, cut ou sed, sur un fichier de plusieurs milliers de lignes c'est l'horreur.
awk est un outil qui est justement fait pour traiter les fichiers ligne à ligne, c'est beaucoup plus efficace.
[^] # Re: Y a pas que Bash dans la vie
Posté par petit_bibi . Évalué à 1.
mais simplement de montrer que l'on peut se passer des pipes en bash et le gain en performances en est conséquent.
Sur de gros fichiers, il est évident que de faire des | grep , | awk ça rox.
Mais sur de petits fichiers et une machine lente, le pipe c'est lent (car fork si je ne me trompe pas).
Et si le processing fait appelle à d'autres outils du script bash pour inserer des trucs dynamiquement , définir des variables pour bash en fonction de certaines lignes, remplir un tableau (si si ça existe en bash) etc ..
Une pile de grep/awk n'est pas forcement plus efficace.
Et puis, le plus pénible avec ces tubes étant la difficulté à obtenir le code de retour de la première commande ce qui rend encors plus complexe la détection d'erreurs dans le script qui sont trop souvent négligées sous pretexte que ça n'arrive jamais.
>imagine que dans ton traitement tu fais appel a des grep, cut ou sed, sur un fichier de plusieurs milliers de lignes c'est l'horreur.
Si je montre comment éviter les grep, cut & autre, ce n'est pas pour en mettre dans le 'processing' de chaques lignes. Suis pas gros débile non plus, hein :-)
Utiliser un script shell pour traiter plusieurs milliers de lignes, c'est pas une super bonne idée non-plus.
# Re:
Posté par IsNotGood . Évalué à 4.
Désolé, mais je n'ai pas tout compris.
Le premier problème qui se pose, est de savoir quel est le domaine d'un shell. Que doit-il supporter ou non.
Je ne pense pas que le shell doit tout faire et être un super language de programmation qui supporte tous les paradigmes.
Comme l'a fait remarquer GeneralZod, il y a des alternatives à sh pour les cas pointus (python, perl, php, etc).
Enfin, il y a le gros problème de la compatibilité si tu fais évoluer sh.
> je me demande alors dans quelle mesure (et je vous pose la question) il ne serait pas intéressant d'avoir un interpréteur shell (type sh ou autre) constamment chargé en mémoire qui pourrait rapidement exécuter n'importe quel script.
C'est un vrai/faux problème. Si Bash est utilisé, alors il est en cache. Son initialisation doit peut-être être optimisé.
Pour les performances, c'est aussi un vrai/faux problème. bash n'est pas utilisé comme programme résident. Il exécute un script et basta. Lorsque mon système est booté, je n'ai plus de bash qui "traine" sauf si je lance xterm, etc...
[^] # Re: Re:
Posté par thedidouille . Évalué à 5.
Je pense que l'exemple de l'init est vraiment mal venu. D'abord chez moi, les scripts sont lancés en parallèle et le démarrage est très rapide, ensuite lors de l'init, ce sont les autres applications lancées qui prennent du temps. Le temps CPU consacré à l'interprétation doit être très faible.
Par exemple :
$ cat > toto1
i=1 ; while [ $i -lt 1000 ] ; do FILE=toto/titi/tata ; echo ${FILE##*/} > /dev/null ; i=$(( $i + 1 )) ; done
$ cat > toto2
i=1 ; while [ $i -lt 1000 ] ; do basename toto/titi/tata > /dev/null ; i=$(( $i + 1 )) ; done
$ time ./toto1
... 0:00.09elapsed ...
$ time ./toto2
... 0:01.36elapsed ...
"basename" est une commande, on voit que la version 100% bash est beaucoup plus rapide. Suffisamment rapide pour ce qu'il est censé faire.
[^] # Re: Re:
Posté par gentildemon . Évalué à 5.
Je pense aussi. Concrètement, pendant le démarrage, plusieurs milliers de processus sont lancés! Et si lancer un processus ça coûte relativement peu, en lancer plusieurs milliers, c'est très coûteux, surtout lorsque le processus s'arrête quasi aussitôt! Il a fallu aller le chercher sur disque (ça, c'est très coûteux en temps), le charger en mémoire, faire l'édition de lien dynamique et donc aller chercher sur disque les librairies,... Lors du démarrage, certains processus mettent peut-être plus de temps à se lancer qu'à faire leur travail!!
On voit d'ailleurs la différence entre un premier lancement et les suivants:
mfertre@paraphrase:~$ time /bin/echo toto
toto
real 0m0.060s
user 0m0.000s
sys 0m0.004s
mfertre@paraphrase:~$ time /bin/echo toto
toto
real 0m0.002s
user 0m0.000s
sys 0m0.000s
Sur un processus qui demande 2 milli-secondes de travail, il en a fallu 64 lors d'un premier appel!
En faisant un calcul tout à fait à la louche, en admettant que 1000 processus correspondant à des exécutables différents soient lancés lors du démarrage (bon, je pense pas quand même, il doit y avoir beaucoup de bash, grep, sed, awk, sort, cut, touch, etc.), ça prendrait environ (64-2ms * 1000) soit environ 1 min (ne soyons plus à une approximation près) ! Sans compter le temps de traitement!
[^] # Re: Re:
Posté par Mildred (site web personnel) . Évalué à 2.
Pas du tout, je ne veux pas faire un shell avec les fonctionnalités d'un langage de programmation complet. je parle juste de la manière dont sont évaluées les variables, c'est tout.
D'ailleurs quand on regarde le langage Jam, il est loin d'être évolué. Cela ne l'empêche pas d'être compatible avec les espaces.
Je pense que le langage de shell doit être simple, et ne pas permettre de faire des erreurs comme le permet sh. Et comme j'aimais bien Jam, je me suis ammusée a faire un shell avec le langage Jam.
Et en passant, mon but ce n'est pas de faire un shell intéractif, mais plutôt de scripts.
Sinon, je viens de penser qu'il me manque les pipes ... Je savais bien que programmer à 3h du matin me ferait oublier quelque chose :) Il faudra voir comment intégrer cela au langage ...
[^] # Re: Re:
Posté par IsNotGood . Évalué à 0.
Pas de problème.
[^] # Re: Re:
Posté par gnumdk (site web personnel) . Évalué à 7.
Bash est compatible avec les espaces.
gnumdk@sarge:~$ for i in a "b${var}c" d; do echo $i; done
a
bmot1 mot2c
d
gnumdk@sarge:~$ IFS=
gnumdk@sarge:~$ for i in a b${var}c d; do echo $i; done
a
bmot1 mot2c
d
Ce qui te fait chier c'est la valeur de IFS.
Mais la bonne méthode quand on code un script est de toujours mettre "" autour d'une variable, meme si on est sur qu'il y'aura pas d'espace, au cas ou pour l'avenir...
# Fonctionnalitées
Posté par CTAF . Évalué à 3.
Ensuite, on l'utilise pour des raisons historiques, bash est présent sur tous les systèmes, du moins on trouve forcement un interpreteur sh.
Ce qui facilite la vie des admins, c'est en partie pour ca que les autotools n'utilisent que des outils unix standard: la portabilité au détriment de la vitesse.
De plus il existe différent shell plus ou moins rapide avec des syntaxes plus ou moins différentes. De ce que tu decris tu as envi d'un équivalent du language c, utilise alors tcsh, csh, ou (celui que je prefere parmis tous) zsh.
au niveau des syntaxes:
sh: sh, bash, zsh
c: zsh, tcsh, csh
k (mélange de csh/sh): ksh
Le shell n'est pas un language pour programmer à mon avis, mais plutot pour bidouiller le système, il est assez difficile de faire des programmes serieux en shell qui soit portable. En loccurence je développe une conf pour bash/ksh/tcsh/zsh qui utilise exactement les mêmes fichiers pour chaques shell, et chaque shell voir version de shell a ses spécificités, j'ai donc une library de fonction portable entre tous les shells.
Et toujours pour en revenir au problèmes principales pour les shells à mon avis, c'est la portabilité, et la donc la disponibilité par default d'un interpreteur sh sur tous les systemes. Sinon pour ce meme projet(il n'y as pas que la configuration des shells) j'aurai utilisé un autre language, et pas question non plus de distribuer des sources a compiler, car par exemple sous cygwin il n'y a pas forcement le compilo d'installé.
PS: le proj en question est www.sf.net/projects/grk
[^] # Re: Fonctionnalitées
Posté par Mildred (site web personnel) . Évalué à 1.
Pour le moment, ce que j'ai fait c'est juste un travail de 5/8 heures, donc il n'y a pas tout. Comme je ne prévoit pas de faire un shell intéractif (le langage jam ne s'y prete pas bien je pense, trop d'espaces), je vais passer sur la completion.
Pour les jobs, on peut déjà lancer un rogramme en arrière plan, il reste a améliorer l'implémentation (si le parent meurt avant ses enfants, wait, disown). Et à part ça, je crois que c'est tout.
Pour les pipes, soit j'utilise la syntaxe '|' (et il faut que je modifie la syntaxe Jam, demande un peu de travail), soit je fais autrement. Ce que je pensait c'est implémenter ça d'une autre manière. Pour les redirections, c'est prévu et pas implémenté.
Je trouve que les pipes sont un peu trop linéaires. En gros une commande ne peut donner son résultat qu'a une seule autre commande, et pas à plusieurs. Je pense que ça pourrait être intéressant de permettre d'avoir plusieurs consommateurs pour un seul producteur.
Pour l'inverse (plusieurs producteurs pour un seul consommateur) ça me paraît plus difficile...
Mais ce qui m'intéresse vraiment, c'est pas forcément de produire quelque chose de viable (je ne pense pas que j'auais le temps de finir mon jam-shell) mais pluutôt de voir ce qui est possible et de rêver un peu. Je trouve l'expansion des variables à la Jam tellement mieux que celle de sh ...
[^] # Re: Fonctionnalitées
Posté par wismerhill . Évalué à 4.
Si ton truc n'est pas interractif alors ce n'est pas un shell, c'est juste un language de script de plus.
# Fish
Posté par patrick_g (site web personnel) . Évalué à 2.
L'article Wikipedia sur Fish : http://en.wikipedia.org/wiki/Friendly_interactive_shell
Je sais pas ou ça en est mais je trouve déjà que l'amélioration de la syntaxe est massive par rapport à Bash.
[^] # Re: Fish
Posté par Mildred (site web personnel) . Évalué à 1.
# heu ...
Posté par Zitune (site web personnel) . Évalué à 1.
exemple :
voila un ptit code code python :
en bash comme prevu :
$ var="mot1 mot2"
$ python test.py a b${var}c d
pas 3 arguments : 4 argument
['a', 'bmot1', 'mot2c', 'd']
mais en zsh (le seul le grand l'unique)
tmp/jeu %var="mot1 mot2"
/tmp/jeu % ./test.py a b${var}c d
3 arguments
['a', 'bmot1 mot2c', 'd']
Donc en zsh .. ben ca marche
[^] # Re: heu ...
Posté par Mildred (site web personnel) . Évalué à 1.
C'est pour ça que je précise bien sh ou bash, car effectivement, il existe plein de shell différents.
par contre pour le coup du zsh tu me surprend, mais effectivement tu as raison. Et moi qui pensait que bash était compatible sh ...
mais, j'hésite à utiliser zsh comme langage de script, car j'ai l'impression que c'est plus un shell intéractif. Et que son comportement peut être différent selon la configuration.
Alors j'hésite a faire quelque chose en zsh qui marchera chez moi (car j'ai certaines option activées ou non) mais qui ne marche pas ailleurs (car d'autres options sont activées). Ceci dit, je n'ai jamais testé et il y a peut être un moyen simple de résoudre ce problème.
# dash
Posté par Eric Heintzmann . Évalué à 2.
Dash est un shell comptible POSIX, il est beaucoup plus petit que bash, et il execute les script plus rapidement que bash.
C'est le shell utilisé par défaut pour /bin/sh dans Ubuntu.
http://en.wikipedia.org/wiki/Debian_Almquist_shell
[^] # Re: Dash vs Linux
Posté par Jack ze . Évalué à 3.
« Madame Chombier, contre votre baril de Linux je vous offre deux barils de Dash !
- Ah ben nan alors ! Je préfère garder mon baril de Linux. »
# Nul !
Posté par Colin Pitrat (site web personnel) . Évalué à 7.
je ne trouve pas ça très logique !
[^] # Re: Nul !
Posté par Mildred (site web personnel) . Évalué à 1.
$(X).txt -> a.txt b.txt c.txt
$(X)z -> az bz cz
Merci de me pointer cette grossière erreur
[^] # Re: Nul !
Posté par Sylvain Sauvage . Évalué à 7.
Je ne sais pour les autres, mais la concaténation me semble quand même beaucoup plus fréquente que la distribution.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.