Aller au menu - Aller au contenu

Icône Le Korn shell

Avatar
Mise à jour : 04/08/2011
Difficulté : Difficile Difficile Creative Commons BY-NC-SA
7 018 visites depuis 7 jours, dont 108 sur ce chapitre classé 32/786
Le ksh, ou Korn shell, du nom de son développeur, fut la réponse d'AT&T aux csh et tcsh, dont il a repris certaines fonctionnalités.

Pour des scripts plus compliqués, avec des fonctions, des flux de redirection (avec des |), des signaux système ou encore des sous-processus, le ksh est parfois plus pratique que ses principaux "concurrents".

Beaucoup d'UNIX l'intègrent par défaut. Sous FreeBSD, vous pouvez l'installer avec :

Code : Console
[Nom de l'ordinateur]# pkg_add -r ksh93


ou

Code : Console
[Nom de l'ordinateur]# cd /usr/ports/shells/ksh93 && make install clean


Le 93 signifie que vous n'installez pas le ksh original, de 1983, mais sa version améliorée de 1993.

Si vous utilisez un UNIX pour qui ksh est présent par défaut, vous commencerez vos scripts par la ligne :

Code : ksh
1
#!/bin/ksh


Par contre, sur un OS comme FreeBSD, pour qui ksh est un shell importé, cette ligne devient :

Code : ksh
1
#!/usr/local/bin/ksh93
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire

A - Saisie, affichage et conditions

Les principes généraux vus avec le csh restent valables en ksh. Il y a toujours des variables, des conditions, des boucles, des calculs, des tableaux, des arguments, etc. Mais les syntaxes changent et s'inspirent moins du C que du précédent shell "maison" d'AT&T : le sh.

Par exemple, pour lire ce que l'utilisateur entre au clavier, on utilise l'instruction read. Notre script saisie (voir le chapitre Vos premiers scripts) deviendra donc, en ksh :

Code : Bash
1
2
3
4
5
#!/usr/local/bin/ksh93

echo "Saisissez un nombre ou un mot : "
read reponse
echo 'Vous avez saisi '$reponse


Je vous rappelle que, contrairement à ce qui est écrit, ces scripts ne sont pas en bash. Cette fois, c'est du ksh.



Le bloc if, lui, est assez différent en ksh :

Code : Bash
1
2
3
4
5
if [[ condition ]]; then
   instructions à exécuter si la condition est vraie
else
   instructions à exécuter si la condition est fausse
fi


Et l'égalité ne se teste pas avec ==. Pour comparer des nombres, on utilise -eq. Et pour tester si deux chaines de caractères sont identiques, c'est un = simple.

Essayez le script devinette.ksh :

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/local/bin/ksh93
                            
echo "Combien font six fois sept ?"
read reponse

if [[ $reponse -eq 42 ]]; then
   echo "C'est la bonne reponse, bravo !"
else
   echo "Non, ce n'est pas la bonne reponse."
fi


Rien ne vous oblige à terminer le nom d'un script ksh par .ksh. Je le fais uniquement pour les distinguer des scripts csh.

Autres conditions que if peut tester :
  • [[ a -ne b ]] : a différent de b
  • [[ a -lt b ]] : a < b
  • [[ a -gt b ]] : a > b

Les symboles && (ET) et || (OU) sont les mêmes qu'en csh. Par contre, si vous voulez inclure l'équivalent d'un else if, il faut écrire elif :

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/local/bin/ksh93

echo "Combien font six fois sept ?"
read reponse

if [[ $reponse -eq 42 ]]; then
   echo "C'est la bonne reponse, bravo !"
elif [[ $reponse -gt 42 ]]; then
   echo "Vous etes au dessus de la bonne reponse."
else
   echo "Vous etes en dessous de la bonne reponse."
fi


Toujours dans les conditions, switch devient case :

Code : Bash
1
2
3
4
5
6
case $variable in
    valeur 1) instruction à exécuter si variable a la valeur 1;;
    valeur 2) instruction à exécuter si variable a la valeur 2;;
    valeur 3|valeur 4) instruction à exécuter si variable a la valeur 3 ou la valeur 4;;
    *) instruction à exécuter si variable n'a aucune des valeurs ci-dessus;;
esac


esac ? késaco ? :o


C'est juste case à l'envers. De la même manière, on referme un if par un fi.

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/local/bin/ksh93
                            
echo "Tapez le nom d'un mois de l'annee en minuscules : "
read mois

case $mois in
   janvier|juin|juillet) print "Ce nom commence par un 'j'.";;
   fevrier) print "Brrr ! Il fait froid !";;
   mars|avril) print "Ne te decouvre pas d'un fil";;
   aout) print "Quelle chaleur !";;
   septembre) print "C'est la rentree.";;
   octobre|novembre) print "Ah... l'automne !";;
   decembre) print "Joyeux Noel !";;
   *) print "On vous a dit le nom d'un mois en minuscules.";;
esac

Vous remarquez la commande print. C'est encore une autre manière d'afficher du texte.

B - Variables, arguments et tableaux

Bien sûr, on n'a pas toujours besoin de read pour affecter une valeur à une variable. On peut aussi lui donner cette valeur directement, avec un simple =. Et si la valeur voulue est une chaîne de caractères, on met des guillemets :

Code : Bash
1
a="champignon"


Pas de set, donc. Par contre, dans ce cas, il ne faut surtout pas mettre d'espaces autour du =. C'est comme ça. Je peux vous dire que ça m'a piégé plus d'une fois, cette bêtise. :pirate:

Pour les calculs, on utilise des double-parenthèses.

Code : Bash
1
2
3
4
5
#!/usr/local/bin/ksh93
                            
a=3
((b=a*2))
echo $b


Ce qui affiche : 6

En ksh, les arguments fournis au script sont traités comme des variables isolées et pas comme un tableau :

$1 est le premier argument. $2 est le deuxième, etc. $# est le nombre total d'arguments. Et, si on veut tous les afficher d'un coup, c'est echo "$*".

Et voici d'autres variables spéciales, qui vous serviront peut-être un jour :
  • $$ : Le PID du script. Si vous voulez faire appel à kill, par exemple.
  • RANDOM : Un nombre au hasard.
  • Les variables d'environnement habituelles.

Les tableaux


On utilise toujours les parenthèses pour les déclarer. Là encore, attention à ne pas laisser d'espaces autour du signe =.


En ksh, la première case d'un tableau ne porte pas le numéro 1 mais le numéro 0.


Code : Bash
1
2
3
4
5
6
7
#!/usr/local/bin/ksh93

nom=(Twm Fluxbox KDE LXDE Xfce GNOME Enlightenment)
print ${nom[*]}
print "premiere case : ${nom[0]}"
print "deuxieme case : ${nom[1]}"
echo "Ce tableau comporte ${#nom[*]} cases."

Quand vous demandez l'affichage du tableau, n'oubliez pas les accolades { }. Elles sont également nécessaires pour une variable simple si vous affichez du texte derrière.

Repérez enfin la variable #nom[*], qui contient le nombre de cases dans le tableau nom.


C - Les boucles

Il y a trois types de boucles en ksh :
  • while
  • until
  • for (équivalent du foreach de csh, et pas des boucles for qu'on rencontre en C).


while


Comme en csh, la boucle while tourne tant qu'une certaine condition est vraie. Sa structure générale est la suivante :

Code : Bash
1
2
3
4
5
6
while [[ condition ]]; do
   instruction à répéter
   ...
   autres instructions à répéter
   ...
done


Parmi les instructions de la boucle, vous rencontrerez peut-être continue et/ou break. Laissez-moi vous les présenter. :)

continue et break n'apparaissent que dans une boucle et servent à l'interrompre prématurément. Mais attention, ils sont différents : continue permet de passer tout de suite au prochain tour de la boucle, sans finir le tour actuel, tandis que break vous fait carrément sortir de la boucle.

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/local/bin/ksh93

i=1
while [[ i -lt 6 ]]; do
   print Tour de boucle numero "$i"
   print Tapez '1 (ou autre chose)' pour continuer ce tour
   print Tapez '2' pour passer au tour suivant
   print Tapez '3' pour quitter la boucle

   read reponse
   case $reponse in
      2) continue;;
      3) break;;
      *) print OK, on continue;;
   esac

   echo Suite de la boucle numero "$i"
   ((i++))
done
print Nous sommes sortis de la boucle.


Ce script donnera :

Code : Console
% boucle.ksh
Tour de boucle numero 1
Tapez 1 (ou autre chose) pour continuer ce tour
Tapez 2 pour passer au tour suivant
Tapez 3 pour quitter la boucle
1
OK, on continue
Suite de la boucle numero 1
Tour de boucle numero 2
Tapez 1 (ou autre chose) pour continuer ce tour
Tapez 2 pour passer au tour suivant
Tapez 3 pour quitter la boucle
f
OK, on continue
Suite de la boucle numero 2
Tour de boucle numero 3
Tapez 1 (ou autre chose) pour continuer ce tour
Tapez 2 pour passer au tour suivant
Tapez 3 pour quitter la boucle
2
Tour de boucle numero 3
Tapez 1 (ou autre chose) pour continuer ce tour
Tapez 2 pour passer au tour suivant
Tapez 3 pour quitter la boucle
3
Nous sommes sortis de la boucle.


Remarquez que le quatrième tour de boucle est annoncé comme le "tour n°3". En effet, à cause du continue, l'instruction ((i++)) n'a pas été lue, cette fois-ci.


until


La boucle until est similaire à while, mais avec cette différence :

Elle tourne jusqu'à ce qu'une certaine condition soit vraie. Ainsi, le script que voici est équivalent au précédent :

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/local/bin/ksh93

i=1
until [[ i -eq 6 ]]; do
   print Tour de boucle numero "$i"
   print Tapez '1 (ou autre chose)' pour continuer ce tour
   print Tapez '2' pour passer au tour suivant
   print Tapez '3' pour quitter la boucle

   read reponse
   case $reponse in
      2) continue;;
      3) break;;
      *) print OK, on continue;;
   esac

   echo Suite de la boucle numero "$i"
   ((i++))
done
print Nous sommes sortis de la boucle.


for


Comme le foreach de csh, la boucle for de ksh affecte successivement plusieurs valeurs à une variable.

Code : Bash
1
2
3
4
for variable in valeur1 valeur2 valeur3 valeur4; do
   instructions à répéter
   ...
done


Dans cet exemple, nous allons faire appel à tradMois, le script csh du chapitre précédent. Vous allez voir que ksh et csh peuvent tout à fait collaborer :

Code : Bash
1
2
3
4
5
#!/usr/local/bin/ksh93

for mois in Jan Feb Mar Apr; do
   echo `tradMois $mois`
done


Résultat :

Code : Console
% exemplefor.ksh
janvier
fevrier
mars
avril


D - Les fonctions

En csh, il n'y avait pas vraiment de fonctions. On pouvait les simuler en appelant un script à l'intérieur d'un autre. C'est ce que je vous ai montré en appelant tradMois à l'intérieur de rapport. Cela nous oblige à découper le programme qu'on veut réaliser en plusieurs fichiers de scripts : un par fonction. Ce n'est pas forcément plus mal : c'est une bonne manière de s'organiser. Mais ça ne plait pas à tout le monde.

En ksh, il y a de vraies fonctions. Et on peut tout mettre dans un seul fichier, si on veut (au risque de ne plus s'y retrouver).

D'accord... Mais c'est quoi, une fonction ? o_O

C'est un petit bout de programme, une série d'instructions, que l'on peut appeler à plusieurs reprises dans le script, en lui donnant éventuellement des arguments.

Pour créer une fonction, on écrit :

Code : Bash
1
2
3
4
mafonction() {
   instructions de la fonction
   ...
}


Et un peu plus loin dans le script, on l'appelle de cette manière :

Code : Bash
1
mafonction argument1 argument2

À l'intérieur de la fonction, on peut utiliser les arguments qu'on lui a transmis. Ils sont désignés par $1, $2, etc. Ce qui signifie qu'on ne peut pas utiliser dans la fonction les arguments du script principal, à moins de les lui transmettre explicitement.


tradMois()


Pour nous exercer, nous allons traduire en ksh le script rapport. Il faut commencer par transformer tradMois en une fonction ksh :

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/local/bin/ksh93

tradMois() {
   court=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
   long=(janvier fevrier mars avril mai juin juillet aout\
         septembre octobre novembre decembre)
   i=0
   until [[ $i -eq 12 ]]; do
      if [[ $1 = ${court[$i]} ]]; then
         echo ${long[$i]}
      fi
      ((i++))
   done

}


#Appel de la fonction, avec l'argument Dec, pour décembre
tradMois Dec

Ce qui affiche : decembre.

On s'est servi d'une boucle until pour faire varier i de 0 à 12. Au début du dernier tour de boucle, i vaut 11. Et à la fin de ce même tour, i = 12, donc la condition du until devient vraie et on n'entre plus dans la boucle.

Le $1 est l'argument que vous avez transmis à la fonction, c'est-à-dire Dec.

Remarquez la différence de notation entre les conditions des lignes 8 et 9. Ligne 8, on compare deux nombres, donc on écrit -eq. Ligne 9, par contre, ce sont des chaines de caractères, donc on utilise un =.

Remarquez aussi que la fonction doit obligatoirement être implémentée (on doit écrire son contenu) avant d'être appelée. En effet, je vous rappelle que les shells sont des langages interprétés donc, si vous l'appelez avant de la décrire, l'ordinateur ne la connaîtra pas.


rapport.ksh


Passons maintenant au script principal. Je vous rappelle que son rôle est d'analyser un fichier et de vous en indiquer le propriétaire, la date de dernère modification, le nombre de lignes, etc. On doit lui donner le nom de ce fichier en argument.

La structure est celle du script csh rapport. Mais vous allez remarquer plusieurs différences syntaxiques. Par exemple, tous les numéros de cases des tableaux sont décalés puisqu'on commence à 1 en csh et à 0 en ksh.

Code : Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/local/bin/ksh93

tradMois() {
   court=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
   long=(janvier fevrier mars avril mai juin juillet aout\
         septembre octobre novembre decembre)
   i=0
   until [[ $i -eq 12 ]]; do
      if [[ $1 = ${court[$i]} ]]; then
         mois=${long[$i]}
         return 3
      fi
      ((i++))
   done
}

tabls=(`ls -l $1`)
tabwc=(`wc $1`)

user=${tabls[2]}
groupe=${tabls[3]}
jour=${tabls[6]}
heure=${tabls[7]}
taille=${tabls[4]}

lignes=${tabwc[0]}
mots=${tabwc[1]}

tradMois ${tabls[5]}

print "Le fichier $1 appartient a l'utilisateur $user et au groupe $groupe.\n\
Sa derniere modification date du $jour $mois a $heure.\n\
Il pese $taille Ko et comporte $mots mots repartis en $lignes lignes.\n"


J'ai modifié légèrement la fonction tradMois() car, cette fois, le but n'est pas d'afficher le nom du mois mais de l'affecter à la variable mois. Et une fois que cette affectation est faite, il est inutile de continuer la boucle. J'ai donc ajouté l'instruction return, qui vous fait sortir immédiatement de la fonction et "retourne" une valeur.

Dans le même genre, il y a l'instruction exit, qui interrompt carrément le script. C'est un peu la sortie de secours si l'un de vos if détecte une erreur.

Là, j'ai choisi de renvoyer la valeur 3. Ce n'est pas très important vu que je ne m'en sers pas après. Sachez cependant que la valeur retournée est ensuite brièvement disponible sous le nom de $?.

$? est à consommer sur place. ;) Sinon, elle se périme tout de suite. La fonction tradMois est appelée ici à la ligne 29. Vous pouvez donc utiliser $? à la ligne 30 (par exemple, echo $?). Mais ensuite, après le print, elle n'est plus disponible.

Essayons le script :

Code : Console
% rapport.ksh saisie.ksh
Le fichier saisie.ksh appartient a l'utilisateur brice et au groupe brice.
Sa derniere modification date du 19 juin a 18:39.
Il pese 500 Ko et comporte 75 mots repartis en 16 lignes.


Bravo ! Vous avez réussi votre première fonction ksh. :D

Je ne vous ai pas présenté ici toutes les possibilités du ksh, ni des scripts en général. Mais vous avez vus les principaux outils, avec lesquels on peut faire pleiiiiin de choses ! Maintenant, à vous de faire travailler votre imagination. ;)
Chapitre précédent Sommaire

Partager

2 commentaires pour "Le Korn shell"
Note moyenne : 3.71 / 4 (299 votes)
Pseudo Commentaire
Hors ligne Night / Sun # Posté le 12/12/2011 à 19:34:29
Avatar

Bonjour,

ce petit cours me semble intéressant.

Néanmoins je me permet d'apporter quelques précisions :
  • -Le Korn Shell a été dévellopé par Alan Korn en 1983, sa première release est le KSH83
  • -Il y a eu une première révision en 1988 : le KSH88
  • -Le KSH93 mentionné ici supporte les tableaux associatifs et l'arithmétique à virgules flottantes, de même que l'approche objet il me semble


Attention, le KSH88 et KSH93 ne sont pas compatibles.

la plupart des unices commerciaux et d'entreprises utilisent le KSH88.

le monde opensource lui utilise le PDKSH (Public Domain Korn Shell) basé sur le KSH88.

Le travail est actuellement en cours pour le rendre pleinement compatible avec le ksh POSIX et celui d'AT&T (lorsqu'ils n'entrent pas en conflit)

sources :

Wikipédia
Package Debian pdksh

Questions :

  • -FreeBSD ne livre pas le pdksh par défaut ?
  • -L'arbre des ports le fournit-il ?
Hors ligne Brice Errandonea # Posté le 12/12/2011 à 20:07:40
Avatar
Groupe : Auteurs

Oui, le pdksh est bien dans l'arbre des ports :

"cd /usr/ports/shells/pdksh/ && make install clean" ou "pkg_add -r pdksh"

Voir tous les commentaires