Bon. J'ai raté la fin des vacances et la rentrée... Mais je suis juste à temps pour la rentrée des vacances de la Toussaint, non ? Jour de la rentrée dans la cour de récréation...
|
Égarement
Le temps des vacances. Flânerie, on écrit quelques lignes de shell-script... Non, je n'ai pas dit avoir codé du shell script pendant mes vacances... mais pendant le temps des vacances... des autres. Nuance.Et hop, une ligne en appelle une autre, du coup voilà une fonction... qui doit faire une boucle et récupérer un résultat facile... Je simplifie et vous épargne les 1 500 lignes de script environnantes !
Oui, je sais, quand on atteint ce volume, on s'est trompé de langage. Python serait probablement plus adapté. Vous ne faites jamais d'erreur vous?
Et une fonction shell, une.
function get_fs() { df | sed -e 1d | awk '{print $1}' | while read FS do FS_ARRAY[$CNT]=$FS CNT=$((CNT + 1)) done } |
Facile. On récupère le premier champ de champ de chaque ligne utile imprimée par df, et on le stocke dans un tableau. Du coup, après on espère pouvoir utiliser le tableau!
Essai
fa# get_fs fa# echo ${FS_ARRAY[0]} fa# Euh, y'a rien là au dessus? |
Pardon? Eh? Où est la valeur de mon tableau?
Oui, je vous vois, tordus, pliés, rigolards.
N'empêche, persévérons
On met donc des traces dans la fonction et on réessaye ! Pour plus de sûreté, on trace l'entrée du tableau après son initialisation et le compteur après son incrémentation.fa# function get_fs() { df | sed -e 1d | awk '{print $1}' | while read FS do FS_ARRAY[$CNT]=$FS echo ${FS_ARRAY[$CNT]} CNT=$((CNT + 1)) echo $CNT done } fa# get_fs /dev/sda1 1 udev 2 tmpfs 3 fa# echo ${FS_ARRAY[0]} fa# Euh, y'a toujours rien là au dessus? |
Souvenirs, souvenirs
Et là, se profile l'idée que peut-être la fonction procéderait au moyen d'un sous-shell, qui initialiserait sa copie de FS_ARRAY... mais que cette copie serait somme toute perdue quand on quitte le sous-shell. Et donc, il faudrait trouver un autre moyen pour remplir ce tableau. Ah, oui le shell, les sous-shells.Mais, vexé, on voudrait quand même savoir si c'est bien un sous-shell la cause de notre perplexité, sans parler de la déception.
Et donc on vérifie!
Imprimons donc le PID de nos shells et sous-shells avec la variable $$ .fa# function get_fs() { df | sed -e 1d | awk '{print $1}' | while read FS do FS_ARRAY[$CNT]=$FS echo shell $$ sets ${FS_ARRAY[$CNT]} CNT=$((CNT + 1)) echo $CNT done } fa# get_fs shell 2130 sets /dev/sda1 shell 2130 sets udev shell 2130 sets tmpfs fa# echo $$ 2130 fa# Euh, Le shell et le sous-shell auraient le même pid? Ou il n'y a pas de sous-shell? Qu'est-ce que cette embrouille? |
Je ne crois même pas ce que je vois !
Comme on subodore une entourloupe et qu'on connaît un peu son système, si on allait chercher nous même le pid du processus? Il faut quand même une bonne dose de paranoïa pour ne plus faire confiance au shell pour quelque chose d'aussi simple que echo $$ !fa# function get_fs() { df | sed -e 1d | awk '{print $1}' | while read FS do read -r MYPID GARBAGE |
echo shell $MYPID $$ sets ${FS_ARRAY[$CNT]}
CNT=$((CNT + 1))
echo $CNT
done
}
fa# get_fs
shell 5707 2130 sets /dev/sda1
shell 5707 2130 sets udev
shell 5707 2130 sets tmpfs
fa# read -r MYPID GARBAGE
fa# echo $MYPID $$
2130 2130
fa# Ah! Ah! Aurais-je trouvé un bug grossièrement trivial? Pourquoi $$ donne-t-il 2130 dans les 2 cas, alors que /proc donne 2130 pour l'un et 5707 pour l'autre?
Donc, le pid du sous-shell diffère de $$ ? Des certitudes s'effondrent. Retour aux sources: RTFM.
RTFM ! Et le manuel nous dit:
$ Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell.
En clair, le manuel nous dit, apprend à lire. En fait, je ne sais pas si j'ai jamais relu cette partie depuis la page manuel d'un bon vieux shell Bourne d'un Unix V7.
Vérification! Un coup d’œil dans un bon vieux manuel V7 trouvé là http://plan9.bell-labs.com/7thEdMan/v7vol1.pdf
$ The process number of this shell.
Ah flûte, le monde évolue ! On a modifié la sémantique de de $$ sans m'avertir ! Serait-il nécessaire de se tenir au courant ?
Simplifions avec BASHPID et BASH_SUBSHELL
fa# function get_fs() { df | sed -e 1d | awk '{print $1}' | while read FS do FS_ARRAY[$CNT]=$FS echo shell $BASHPID $BASH_SUBSHELL $$ sets ${FS_ARRAY[$CNT]} CNT=$((CNT + 1)) echo $CNT done } fa# get_fs shell 5715 1 2130 sets /dev/sda1 shell 5715 1 2130 sets udev shell 5715 1 2130 sets tmpfs fa# echo $BASHPID $BASH_SUBSHELL $$ 2130 0 2130 fa# |
Voilà! $BASHPID est la variable qui indique réellement le pid du processus shell courant. et $BASH_SUBSHELL est une indication d'un niveau de sous-shell. A utiliser la prochaine fois qu'un cas douteux se présentera.
Tatillons
Je vous connais, lecteurs tatillons ! D'accord, il sait maintenant déterminer si le code est exécuté dans un sous-shell ou non, mais il ne peut toujours pas récupérer le tableau initialisé par la fonction dans le shell principal. Voilà un moyen comme un autre.fa# function get_fs() { for FS in $(df | sed -e 1d | awk '{print $1}') do FS_ARRAY[$CNT]=$FS echo shell $BASHPID $BASH_SUBSHELL $$ sets ${FS_ARRAY[$CNT]} CNT=$((CNT + 1)) done } fa# get_fs shell 6432 0 6432 sets /dev/sda1 shell 6432 0 6432 sets udev shell 6432 0 6432 sets tmpfs fa# echo$CNT ${FS_ARRAY[0]} 7 /dev/sda1 fa# Ah, enfin! |
Bref
Le shell (et spécialement les sous-shells) contrairement au vélo, ça s'oublie.Il faut toujours lire les manuels des nouvelles versions.