sync sur un seul fichier

Le
Daniel Caillibaud
Bonjour,

J'ai de temps en temps un souci dans des scripts bash avec des écritur=
es dans un fichier pas
encore "vues" au moment où je regarde si y'en a eu.

En gros, du

# stdout dans un rapport (avec préfixe de date)
exec > >(while read line; do echo "[$(date '+%F %T')] $line" >> $RAPPORT; d=
one)

echo "un truc"
…
# si le rapport est non vide on l'envoie
[ -s $RAPPORT ] && mail -s "rapport du $(date +%F)" root < $RAPPORT

Mais le test ci-dessus marche pas toujours, le fichier est parfois vu vide =
au moment du test
alors qu'il y a bien eu une écriture de lancée (un test [ $(wc -l=
< $RAPPORT) -gt 0 ] ne fait
pas mieux).
C'est probablement lié à mon exec qui récupère la sorti=
e standard et se fait visiblement en
asynchrone, ou ne met pas à jour les infos en live, je sais pas exacte=
ment.

Ajouter un sleep 1 avant de tester règle en général le pb, m=
ais ça me parait pas très propre
ni très fiable (pourquoi 1, pas sûr que ça suffise toujours,=
etc.).

Un sync avant de tester fonctionnerait probablement, mais c'est trop violen=
t (ça synchronise
tout le filesystem, sur une machine avec un moteur de base de données =
chargé ça peut figer un
peu trop longtemps les I/O à un mauvais moment).

Une idée pour faire ça correctement ?

Merci

--
Daniel
Vidéos High-Tech et Jeu Vidéo
  • Le système d'infotainment Ford Sync passe en version 3 et change de plate-forme, passant ...
  • Sync, une fonction de gestion vocale des SMS qui sera intégrée dans les véhicules de ...
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Olivier Bitsch
Le #26386195
--001a113f1d5a7f4034052a2ab9e4
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Hello,

Alors je ne connaissais pas l'utilisation d'exec dans les scripts bash, et
ça me parrait très pratique (visiblement pour faire de la redirec tion de
toutes les sorties des commandes d'un script).

Je me suis un peu penché sur la question, et effectivement, tant que l e
script n'est pas terminé, il semblerait que le fichier $RAPPORT soit
toujours ouvert, et son contenu peut ne pas être sauvegardé (surt out si le
filesystem utilise noatime il me semble). Mais techniquement, même ave c
cette option, lorsque le script est terminé, le contenu du fichier $RA PPORT
doit être présent, même si ce dernier n'est pas encore rà ©ellement écrit sur
le disque.

D'après ce petit guide sur exec :

http://wiki.bash-hackers.org/commands/builtin/exec

Il faudrait mieux tout mettre ton script dans une fonction (main dans
l'exemple), lancer la fonction et récupérer le résultat dans le fichier de
sortie puis enfin analyser le fichier de sortie.

main() {

echo "un truc"

}

main 2>&1 | while read line; do echo "[$(date '+%F %T')] $line" >> $RAPPORT
; done


# si le rapport est non vide on l'envoie
[ -s $RAPPORT ] && mail -s "rapport du $(date +%F)" root < $RAPPORT

Le 25 janvier 2016 à 10:52, Daniel Caillibaud
Bonjour,

J'ai de temps en temps un souci dans des scripts bash avec des écrit ures
dans un fichier pas
encore "vues" au moment où je regarde si y'en a eu.

En gros, du

# stdout dans un rapport (avec préfixe de date)
exec > >(while read line; do echo "[$(date '+%F %T')] $line" >> $RAPPORT;
done)

echo "un truc"
…
# si le rapport est non vide on l'envoie
[ -s $RAPPORT ] && mail -s "rapport du $(date +%F)" root < $RAPPORT

Mais le test ci-dessus marche pas toujours, le fichier est parfois vu vid e
au moment du test
alors qu'il y a bien eu une écriture de lancée (un test [ $(wc -l <
$RAPPORT) -gt 0 ] ne fait
pas mieux).
C'est probablement lié à mon exec qui récupère la sor tie standard et se
fait visiblement en
asynchrone, ou ne met pas à jour les infos en live, je sais pas exac tement.

Ajouter un sleep 1 avant de tester règle en général le pb, mais ça me
parait pas très propre
ni très fiable (pourquoi 1, pas sûr que ça suffise toujour s, etc.).

Un sync avant de tester fonctionnerait probablement, mais c'est trop
violent (ça synchronise
tout le filesystem, sur une machine avec un moteur de base de donnée s
chargé ça peut figer un
peu trop longtemps les I/O à un mauvais moment).

Une idée pour faire ça correctement ?

Merci

--
Daniel





--001a113f1d5a7f4034052a2ab9e4
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<div dir="ltr"><div><div><div><div>Hello,<br><br></div>Alors je ne connai ssais pas
l&#39;utilisation d&#39;exec dans les scripts bash, et ça me parrait t rès
pratique (visiblement pour faire de la redirection de toutes les sorties
des commandes d&#39;un script).<br><br></div>Je me suis un peu penché sur
la question, et effectivement, tant que le script n&#39;est pas terminà ©, il
semblerait que le fichier $RAPPORT soit toujours ouvert, et son contenu
peut ne pas être sauvegardé (surtout si le filesystem utilise noa time il
me semble). Mais techniquement, même avec cette option, lorsque le
script est terminé, le contenu du fichier $RAPPORT doit être pr ésent,
même si ce dernier n&#39;est pas encore réellement écrit sur le disque. faudrait mieux tout mettre ton script dans une fonction (main dans
l&#39;exemple), lancer la fonction et récupérer le résultat dans le fichier
de sortie puis enfin analyser le fichier de sortie.<br><br><p>
<span style="font-family:monospace,monospace">main() {
</span></p><span style="font-family:monospace,monospace">
</span><pre><span style="font-family:monospace,monospace"> echo &quot;un truc&quot;</span></pre><span style="font-family:monospace,monospace">

</span><p><span style="font-family:monospace,monospace">
}
</span></p><span style="font-family:monospace,monospace">

</span><p><span style="font-family:monospace,monospace">
main 2&gt;&amp;1 | while read line; do echo &quot;[$(date &#39;+%F %T&#39;) ] $line&quot; &gt;&gt; $RAPPORT ; done</span></p><p><br># si le rapport est non vide on l&#39;envoie<br>
[ -s $RAPPORT ] &amp;&amp; mail -s &quot;rapport du $(date +%F)&quot; root &lt; $RAPPORT <br>
J&#39;ai de temps en temps un souci dans des scripts bash avec des écr itures dans un fichier pas<br>
encore &quot;vues&quot; au moment où je regarde si y&#39;en a eu.<br>
<br>
En gros, du<br>
<br>
# stdout dans un rapport (avec préfixe de date)<br>
exec &gt; &gt;(while read line; do echo &quot;[$(date &#39;+%F %T&#39;)] $l ine&quot; &gt;&gt; $RAPPORT; done)<br>
<br>
echo &quot;un truc&quot;<br>
…<br>
# si le rapport est non vide on l&#39;envoie<br>
[ -s $RAPPORT ] &amp;&amp; mail -s &quot;rapport du $(date +%F)&quot; root &lt; $RAPPORT<br>
<br>
Mais le test ci-dessus marche pas toujours, le fichier est parfois vu vide au moment du test<br>
alors qu&#39;il y a bien eu une écriture de lancée (un test [ $(w c -l &lt; $RAPPORT) -gt 0 ] ne fait<br>
pas mieux).<br>
C&#39;est probablement lié à mon exec qui récupère la s ortie standard et se fait visiblement en<br>
asynchrone, ou ne met pas à jour les infos en live, je sais pas exacte ment.<br>
<br>
Ajouter un sleep 1 avant de tester règle en général le pb, m ais ça me parait pas très propre<br>
ni très fiable (pourquoi 1, pas sûr que ça suffise toujours, etc.).<br>
<br>
Un sync avant de tester fonctionnerait probablement, mais c&#39;est trop vi olent (ça synchronise<br>
tout le filesystem, sur une machine avec un moteur de base de données chargé ça peut figer un<br>
peu trop longtemps les I/O à un mauvais moment).<br>
<br>
Une idée pour faire ça correctement ?<br>
<br>
Merci<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
Daniel<br>
<br>
</font></span></blockquote></div><br></div>

--001a113f1d5a7f4034052a2ab9e4--
Daniel Caillibaud
Le #26386207
Le 25/01/16 à 17:04, Olivier Bitsch
OB> Hello,
OB>
OB> Alors je ne connaissais pas l'utilisation d'exec dans les scripts bash, et
OB> ça me parrait très pratique (visiblement pour faire de la red irection de
OB> toutes les sorties des commandes d'un script).
OB>
OB> Je me suis un peu penché sur la question, et effectivement, tant q ue le
OB> script n'est pas terminé, il semblerait que le fichier $RAPPORT so it
OB> toujours ouvert, et son contenu peut ne pas être sauvegardé ( surtout si le
OB> filesystem utilise noatime il me semble). Mais techniquement, même avec
OB> cette option, lorsque le script est terminé, le contenu du fichier $RAPPORT
OB> doit être présent, même si ce dernier n'est pas encore r éellement écrit sur
OB> le disque.
OB>
OB> D'après ce petit guide sur exec :
OB>
OB> http://wiki.bash-hackers.org/commands/builtin/exec
OB>
OB> Il faudrait mieux tout mettre ton script dans une fonction (main dans
OB> l'exemple), lancer la fonction et récupérer le résultat dans le fichier de
OB> sortie puis enfin analyser le fichier de sortie.
OB>
OB> main() {
OB>
OB> echo "un truc"
OB>
OB> }
OB>
OB> main 2>&1 | while read line; do echo "[$(date '+%F %T')] $line" >> $RAP PORT
OB> ; done
OB>
OB>
OB> # si le rapport est non vide on l'envoie
OB> [ -s $RAPPORT ] && mail -s "rapport du $(date +%F)" root < $RAPPORT

Merci pour avoir pointé ça.

Ça va pas être très pratique de reprendre tous mes scripts, mais y'a peut-être pas d'autre
solution.
Si le pb est que le sous-shell n'a pas refermé son fichier, terminer l e exec avant d'analyser
$RAPPORT devrait marcher aussi.
Ça simplifie pas tellement mon pb, parce la souvent je continue d'util iser la capture stdout
après l'envoi par mail (pour confirmer qu'il a été envoyà ©, éventuellement faire d'autre choses
et avant de sortir concaténer tout ça dans un log global).

En revanche, je m'étonne que le shell principal ne récupère pas les infos qu'un sous-shell
aurait écrites dans un fichier (il me semblait que même si elle n 'était pas encore
physiquement écrites sur le disque ça devait fonctionner, mais vi siblement c'est pas très
fiable), ou alors ça ne fonctionne que si le sous-shell a terminé et fermé son descripteur de
fichier.

Il me semble aussi avoir lu que c'était même la seule manièr e d'échanger des valeurs entre
un sous-shell et son parent[1], ça doit être vrai tant que le sou s-shell ne tourne pas en tâche
de fond (attendre qu'il ait fini avant de regarder le résultat).

Faudrait que j'essaie en exportant une fct d'écriture (dans mon fichie r $RAPPORT) depuis le
script principal vers le sous-shell…

Sinon, y'a peut-être une autre solution en utilisant par un descripteu r de fichier en lecture
(écrire dedans plutôt que dans $RAPPORT) ou alors en utilisant un signal pour signifier au
shell parent que l'on a écrit qqchose dans $RAPPORT, mais il faudrait alors fermer le
descripteur qui capture sdtout pour lire le contenu, on tourne en rond (et ça devient une usine
à gaz).

Moralité, la solution que tu mentionnes est probablement la plus simpl e, quitte à faire
des fcts main1, main2…


[1] on peut s'en sortir en passant par des signaux, je m'en sers parfois ma is ça limite le nb
de valeurs différentes que l'on peut passer, le parent ne peut que cho isir parmi une liste
connue à l'avance, avec une correspondance signal<=>valeur, pour deu x valeurs possible ça
marche bien (j'utilise RTMIN et RTMAX, mais y'en a quelques autres de dispo pour ce genre
d'usage).

--
Daniel

Les extrémistes devraient être fusillés.
Daniel Caillibaud
Le #26386442
Le 25/01/16 à 18:11, Daniel Caillibaud DC> Faudrait que j'essaie en exportant une fct d'écriture (dans mon fi chier $RAPPORT) depuis le
DC> script principal vers le sous-shell…

ça change rien

DC> Sinon, y'a peut-être une autre solution en utilisant par un descri pteur de fichier en
DC> lecture (écrire dedans plutôt que dans $RAPPORT) ou alors en utilisant un signal pour
DC> signifier au shell parent que l'on a écrit qqchose dans $RAPPORT, mais il faudrait alors
DC> fermer le descripteur qui capture sdtout pour lire le contenu, on tourn e en rond (et ça
DC> devient une usine à gaz).
DC>
DC> Moralité, la solution que tu mentionnes est probablement la plus s imple, quitte à faire
DC> des fcts main1, main2…

Sauf que si je veux séparer stdout et stdErr vers des pipes diffé rents je sais pas faire sans
sous-shell.

Et le sous-shell qui se ferme ne suffit pas à garantir que le contenu du fichier est vu
correctement par le shell parent, faut aussi "attendre un peu".

Exemple, un fichier test.sh

#!/bin/bash

LOGERR=err
LOGSTD=out

$LOGERR
$LOGSTD



myPid=$$
errors=no

function fin() {
[ -s $LOGSTD ] && echo plein || echo vide
[ $(wc -l < $LOGSTD) -gt 0 ] && echo plein || echo vide
[ "$errors" == "yes" ] && echo "erreurs détectées" || echo "pas d'erreur détectée"
}

trap "errors=yes" RTMIN
trap fin EXIT

exec > >(while read line; do echo "[$(date '+%F %T')] $line" >> $LOGSTD; do ne)
exec 2> >(kill -RTMIN $myPid; while read line; do echo "[$(date '+%F %T')] ERROR : $line"|tee
-a $LOGERR >> $LOGSTD; done)

echo erreur >&2
fin
echo "sortie"



en le lançant et regardant le fichier juste après, ça donne :

./test.sh; cat out; sleep 0.2; echo; cat out
[2016-01-26 19:26:24] vide
[2016-01-26 19:26:24] ERROR : erreur
[2016-01-26 19:26:24] vide

[2016-01-26 19:26:24] vide
[2016-01-26 19:26:24] ERROR : erreur
[2016-01-26 19:26:24] vide
[2016-01-26 19:26:24] erreurs détectées
[2016-01-26 19:26:24] sortie
[2016-01-26 19:26:24] plein
[2016-01-26 19:26:24] plein
[2016-01-26 19:26:24] erreurs détectées

Mais si je met le kill après l'écriture dans le sous-shell avec
exec 2> >(while read line; do echo "[$(date '+%F %T')] ERROR : $line"|tee - a $LOGERR >>
$LOGSTD; done; kill -RTMIN $myPid;)

ça donne

./test.sh: ligne 22 : kill: (23702) - Aucun processus de ce type
[2016-01-26 19:30:46] vide
[2016-01-26 19:30:46] ERROR : erreur
[2016-01-26 19:30:46] vide

[2016-01-26 19:30:46] vide
[2016-01-26 19:30:46] ERROR : erreur
[2016-01-26 19:30:46] vide
[2016-01-26 19:30:46] pas d'erreur détectée
[2016-01-26 19:30:46] sortie
[2016-01-26 19:30:46] plein
[2016-01-26 19:30:46] plein
[2016-01-26 19:30:46] pas d'erreur détectée

bizarre non ? Le processus principal est déjà fini (plus de pid) alors que le kill du sous-shell
n'a pas terminé…

Je vais arrêter là et garder mon sleep…

--
Daniel

Bien sûr l'Amérique avait été découverte avant Col omb.
Mais le secret avait été bien gardé.
Oscar Wilde
Publicité
Poster une réponse
Anonyme