#1 Le 15/05/2020, à 23:36
- alex2423
Le fils le plus rapide donne des nouvelles à son père immédiatement
Hello tout le monde,
Mon titre n'est peut être pas très explicite.
C'est l'histoire d'un script père qui a 2 fils. Un des 2 fils est toujours plus rapide que l'autre pour faire ses taches.
Comment pour faire en sorte que le fils le plus rapide donne immédiatement des nouvelles à son père sans qu'il attende son frère, bien plus lent. Cela peut paraître un peu égoiste, désolé.
Le père et ses 2 fils :
function fils1() {
sleep 5
echo "FIN FILS1"
}
function fils2() {
sleep 10
echo "FIN FILS2"
}
echo "PERE sous shell pere $BASH_SUBSHELL"
echo "PERE sous shell fils2 $BASHPID"
retour_fils1=$((fils1) &)
retour_fils2=$((fils2) &)
echo "Chez le pere : fils1 = ${retour_fils1}, fils2 = ${retour_fils2}"
Et voilà ce que j'obtiens :
xbionic@bionic:~/Documents/script$ bash -x multi_process.sh
+ variable_globale=
+ echo 'PERE sous shell pere 0'
PERE sous shell pere 0
+ echo 'PERE sous shell fils2 5510'
PERE sous shell fils2 5510
++ fils1
++ sleep 5
++ echo 'FIN FILS1'
+ retour_fils1='FIN FILS1' <==
++ fils2
++ sleep 10
++ echo 'FIN FILS2'
+ retour_fils2='FIN FILS2'
+ echo 'Chez le pere : fils1 = FIN FILS1, fils2 = FIN FILS2'
Chez le pere : fils1 = FIN FILS1, fils2 = FIN FILS2
Je m'attendais comme résultat
Chez le pere : fils1 = FIN FILS1, fils2 = FIN FILS2
Je m'attendais à ce que le fiston le plus rapide (ici "fils1") ayant terminé sa tache en premier, donne l'info immédiatement après à son père sans attendre fréro (ici "fils2") bien plus lent, pour communiquer tous les 2 l'info. Je m'attendais à ce que la variable ${retour_fils2} soit vide, non renseigné.
Hors ligne
#2 Le 16/05/2020, à 00:03
- Zakhar
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
C'est un "bashism" qu'une fonction soit parallélisée ?
A mon sens ici tu n'as rien en parallèle...
Pour paralléliser il faut relancer ton script lui-même avec le & à la fin.
Aussi tu ne peux pas dans ce cas récupérer le résultat dans une variable puisque le "résultat" est en fait le stdout. Donc si tu attends le stdout du fils, de fait tu n'as plus de parallélisme.
Donc pour faire ce que tu désires, la sortie des fils doit être mis dans des fichiers, et il faut ensuite relire les fichiers pour voir ce qu'il y a dedans.
Après tu peux jouer avec les fonctions de "job" pour inspecter les fils terminés et ceux qui sont en cours et réagir en conséquence.
Bref : remaniement total du script !
"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)
Hors ligne
#3 Le 16/05/2020, à 08:45
- alex2423
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
C'est un "bashism" qu'une fonction soit parallélisée ?
La fonction que je souhaite parallélisé est une fonction socle dans mon script, reprise plein de fois.
Rien empêche de créer des scripts externe et de "sourcer" (du style ". /mes_fonctions.sh") sur ma librairie maison mais je trouve que c'est ce compliqué la vie.
Pour paralléliser il faut relancer ton script lui-même avec le & à la fin.
Aussi tu ne peux pas dans ce cas récupérer le résultat dans une variable puisque le "résultat" est en fait le stdout. Donc si tu attends le stdout du fils, de fait tu n'as plus de parallélisme.Donc pour faire ce que tu désires, la sortie des fils doit être mis dans des fichiers, et il faut ensuite relire les fichiers pour voir ce qu'il y a dedans.
Okay, je comprends je n'avais pas la bonne logique.
Je créé des fils qui font leurs vies tout seul mais le point de blocage était au moment de l'affectation sur le processus père : retour_fils1=$((fils1) &)
D'ailleurs, je n'avais pas bien fait attention, en mode débug, on remarque que cela se bloque au niveau de l'affectation. La première affectation se déroule assez rapidement puis après il y a temporisation pour attendre l'affectation suivante pour le fils2.
Et après reflexion, cela me permet logique, une fois retourné chez le père, toutes les commandes synchrone et donc c'est normal que l'on attende le résultat des commandes les uns après les autres.
Il faudrait dans ce cas, créé les fils et c'est ensuite au père de regarder l'état de ses fils, cela doit être de l'initiative du père. Et non pas l'inverse du fils au père
Comme tu me l'a conseillé, je suis donc passé par un fichier pour que le père puisse récupérer l'état des fils
Le nouveau code :
function fils1() {
export FILS=FILS1
sleep 5
echo "FIN FILS1" 1>tmp/fils1
}
function fils2() {
export FILS=FILS2
sleep 10
echo "FIN FILS2" 1>tmp/fils1
}
(fils1) & 1>tmp/fils1
(fils2) & 1>tmp/fils2a
while true
do
if [[ $(cat tmp/fils1) = "FIN FILS1" ]]
then
retourFils=$(cat tmp/fils1)
echo "fin fils1, je sors"
break
elif [[ $(cat tmp/fils2) = "FIN FILS2" ]]
then
retourFils=$(cat tmp/fils2)
echo "fin fils2, je sors"
break
fi
done
echo "PERE : fils reour : $retourFils"
echo "variable en : $FILS"
Le code est donné pour exemple, utiliser des break par exemple n'est pas propre du tout.
Et voilà le résultat
xbionic@bionic:~/Documents/script/$ bash multi_process.sh
fin fils1, je sors
PERE : fils reour : FIN FILS
variable en :
On remarque cette fois-ci que l'on a tout de suite, le résultat de FILS1 et après on sort.
J'ai voulu tester avec les variables d'environnements mais malheureusement les sous shell des fils sont bien hermétiques.
Après tu peux jouer avec les fonctions de "job" pour inspecter les fils terminés et ceux qui sont en cours et réagir en conséquence.
Cela pourrait être une solution plus propre mais malheureusement dans mon cas, je souhaite récupérer des coordonnées et celles-ci dépassent quasiment tout le temps les 255. Et je crois que l'on est limité à ce nombre dans le exit
Bref la seule est de passer par des fichiers, ce n'est pas très propre mais bon il semblerait que ce soit le seul moyen.
Hors ligne
#4 Le 16/05/2020, à 11:06
- Hizoka
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Sinon, l'autre idée serait de passer par un FIFO.
Ton script principal lance les commandes en lisant les retours FIFO et les scripts appelés écrivent dessus.
En fonction de ce que lit le script principal, tu peux produire des actions genre :
while read Retour
do
case ${Retour} in
bonjour) ... ;;
esac
done < fifo
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#5 Le 18/05/2020, à 22:11
- Zakhar
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Sinon, l'autre idée serait de passer par un FIFO.
Effectivement, solution élégante si les fils écrivent des messages avec un retour à la ligne !
"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)
Hors ligne
#6 Le 18/05/2020, à 23:07
- Hizoka
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Modifier l'IFS du while ne permettrait pas de régler ce problème ?
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#7 Le 19/05/2020, à 08:04
- marcus68
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Bonjour,
en m'inspirant des différents codes de ce fil, je vous propose cette solution (en bash) qui ne passe pas par des fichiers temporaires :
function fils1() {
sleep 2
echo "FIN FILS1"
}
function fils2() {
sleep 5
echo "FIN FILS2"
}
while read a
do
case "$a" in
"FIN FILS1") echo "fils 1 plus rapide";break ;;
"FIN FILS2") echo "fils 2 plus rapide";break ;;
esac
done <<< $(fils1&fils2)
Dernière modification par marcus68 (Le 19/05/2020, à 08:08)
Hors ligne
#8 Le 19/05/2020, à 09:19
- Hizoka
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Avec ta façon, on attend que les 2 aient terminé.
Avec done < <(fils1&fils2), dés qu'un des 2 à terminé, il affiche le résultat.
Ça dépend des besoins
while read a
do
case "$a" in
"FIN FILS1") echo "fils 1 terminé" ;;
"FIN FILS2") echo "fils 2 terminé" ;;
esac
done < <(fils1&fils2)
Affichera fils 1 terminé puis fils 2 terminé.
Pratique pour traiter les retours au fur et à mesure.
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#9 Le 19/05/2020, à 09:41
- marcus68
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Le break interrompt le boucle, il s'arrête donc quand l'un ou l'autre renvoi le texte correspondant. Il n'y a donc pas de nécessité que les 2 aient fini pour que le script continue.
EDIT : non en effet, ça attends quand même
Dernière modification par marcus68 (Le 19/05/2020, à 09:51)
Hors ligne
#10 Le 19/05/2020, à 09:58
- Hizoka
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
Hé hé hé
J'avais fait mon petit test car je n'étais pas sûr
KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github
Hors ligne
#11 Le 24/05/2020, à 14:05
- LeoMajor
Re : Le fils le plus rapide donne des nouvelles à son père immédiatement
bonjour
tu peux aussi remplacer le echo de fin de traitement par un signal, qui est récupéré par trap, ce qui se traduit par une simulation d'un évènement, d'un genre différent de while read
d'après une idée de @totor
cat timer.bash
#!/bin/bash
#https://forum.ubuntu-fr.org/viewtopic.php?pid=21443151#p21443151
_SHELL_PID=
_SHELL_SIG=SIGUSR1
_SHELL_TIMESTAMP=60
function startTimer {
while sleep ${_SHELL_TIMESTAMP}s
do
# on envoie le signal uniquement si le process existe toujours
ps -p ${_SHELL_PID} &>/dev/null || return 0
builtin kill -${_SHELL_SIG} ${_SHELL_PID}
#event= raise ${_SHELL_PID} ${_SHELL_SIG} && action "$@"
done
}
# vérification des paramétres
while getopts :s:p:t: option
do
case "${option}" in
p) _SHELL_PID=${OPTARG};;
s) _SHELL_SIG=${OPTARG};;
t) _SHELL_TIMESTAMP=${OPTARG};;
:) echo "Argument manquant pour l'option '${OPTARG}'" >&2
exit 1;;
"?") echo "Option non valide : ${OPTARG}." >&2
exit 1;;
esac
done
# vérifications liées au PID
# par défaut, le PID est le PID du parent
[[ ${_SHELL_PID} ]] || _SHELL_PID=${PPID}
# vérification du format & de l'existence du process
[[ ${_SHELL_PID//[0-9]} ]] && {
# erreur dans le format attendu
echo "Numéro de PID '${_SHELL_PID}' erroné !" >&2
exit 1
}
# le PID existe-t-il ?
ps -p ${_SHELL_PID} &>/dev/null || {
echo "Aucun processus pour le PID '${_SHELL_PID}'" >&2
exit 1
}
# Vérification du signal à passer
[[ ${_SHELL_SIG} ]] || _SHELL_SIG=SIGUSR1
# précautions d'usage
_SHELL_SIG="${_SHELL_SIG^^}"
_SHELL_SIG="SIG${_SHELL_SIG#SIG}"
_SIG_LISTE="$(builtin kill -l)"
# on vérifie si c'est un signale valide
[[ "${_SIG_LISTE}" == *${_SHELL_SIG}\)* || "${_SIG_LISTE}" == *\)\ ${_SHELL_SIG}[[:space:]]* ]] || {
echo "Signal '${_SHELL_SIG}' non valide !" >&2
exit 1
}
# vérification du délai
# par défaut, le timestamp est de 60sec
[[ ${_SHELL_TIMESTAMP} ]] || _SHELL_TIMESTAMP=60
[[ ${_SHELL_TIMESTAMP//[0-9.]} ]] && {
# erreur dans le format attendu
echo "Délai '${_SHELL_TIMESTAMP}' non valide !" >&2
exit 1
}
startTimer
cat timer-raise-event
#!/bin/bash
trap event SIGUSR1
count=0
last=$count
limit=10 #nb de fois maximum que l'évènement se répète
timer_delay=0.5 # fréquence, sensibilité de l'evènement, en secondes
function event {
((count++))
echo "$count:signal:$(uuidgen)"
}
coproc TIMER { ./timer.bash -t "$timer_delay" -p $$ ; }
ps h -p $$ -o comm,cmd,ppid,pid
_PID=${TIMER_PID}
echo ${_PID}
ps h -p ${_PID} -o comm,cmd,ppid,pid
function eloop {
if [ "$limit" -lt 0 ]; then
echo "no limit"
while true; do if [ "x$last" != "x$count" ]; then last="$count"; echo "$last" ; fi; done; echo "no limit"
elif [ "$limit" -eq 1 ]; then
until [ "$count" -ge "$limit" ]; do if [ "x$last" != "x$count" ]; then last="$count"; echo "$last" ; fi; done; echo "oneshot"
elif [[ "$limit" =~ [0-9]+ ]]; then
until [ "$count" -ge "$limit" ]; do if [ "x$last" != "x$count" ]; then last="$count"; echo "$last" ; fi; done; echo "only $limit, events"
fi
}
eloop "$limit"
./timer-raise-event
dans une autre console
sudo forkstat
Hors ligne