Forcer le vidage de la sortie vers un fichier pendant que le script bash est toujours en cours d'exécution

89

J'ai un petit script, qui est appelé quotidiennement par crontab en utilisant la commande suivante:

/homedir/MyScript &> some_log.log

Le problème avec cette méthode est que some_log.log n'est créé qu'une fois MyScript terminé. Je voudrais vider la sortie du programme dans le fichier pendant son exécution afin que je puisse faire des choses comme

tail -f some_log.log

et suivre les progrès, etc.

olamundo
la source
Nous aurons besoin d'une description -ou si possible du code- de ce que fait exactement votre petit script ...
ChristopheD
7
Pour supprimer le tampon des scripts python, vous pouvez utiliser "python -u". Pour supprimer le tampon des scripts Perl, voir la réponse de Greg Hewgill ci-dessous. Et ainsi de suite ...
Eloici
Si vous pouvez modifier le script, vous pouvez généralement vider le tampon de sortie explicitement dans un script, par exemple en python avec sys.stdout.flush().
drevicko

Réponses:

28

bash lui-même n'écrira jamais aucune sortie dans votre fichier journal. Au lieu de cela, les commandes qu'il invoque dans le cadre du script écriront chacune individuellement la sortie et videront chaque fois qu'elles le souhaitent. Votre question est donc de savoir comment forcer les commandes du script bash à se vider, et cela dépend de ce qu'elles sont.

Chris Dodd
la source
23
Je ne comprends vraiment pas cette réponse.
Alfonso Santiago
2
Pour une meilleure idée du comportement de la sortie standard, consultez stackoverflow.com/a/13933741/282728 . Une version courte - par défaut, s'il est redirigé vers un fichier, stdout est entièrement mis en mémoire tampon; il est écrit dans un fichier uniquement après un flush. Stderr ne l'est pas - il est écrit après chaque '\ n'. Une solution consiste à utiliser la commande 'script' recommandée par user3258569 ci-dessous, pour que stdout soit vidé après chaque fin de ligne.
Alex
Déclarer l'évidence, et dix ans plus tard, mais c'est un commentaire, pas une réponse, et cela ne devrait pas être la réponse acceptée.
RealHandy il y a
84

J'ai trouvé une solution à cela ici . En utilisant l'exemple de l'OP, vous exécutez essentiellement

stdbuf -oL /homedir/MyScript &> some_log.log

puis le tampon est vidé après chaque ligne de sortie. Je combine souvent cela avec nohuppour exécuter de longs travaux sur une machine distante.

stdbuf -oL nohup /homedir/MyScript &> some_log.log

De cette façon, votre processus n'est pas annulé lorsque vous vous déconnectez.

Martin Wiebusch
la source
1
Pourriez-vous ajouter un lien vers une documentation pour stdbuf? Sur la base de ce commentaire, il semble qu'il ne soit pas disponible sur certaines distributions. Pourriez-vous clarifier?
Fund Monica's Lawsuit
1
stdbuf -o ajuste la mise en mémoire tampon stdout. Les autres options sont -i et -e pour stdin et stderr. L définit la mise en tampon de ligne. On pourrait également spécifier la taille du tampon, ou 0 pour aucune mise en mémoire tampon.
Seppo Enarvi
5
ce lien n'est plus disponible.
Fzs2
2
@NicHartley: stdbuffait partie de GNU coreutils, la documentation peut être trouvée sur gnu.org
Thor
Au cas où cela aiderait quelqu'un, utilisez export -f my_function et ensuite stdbuf -oL bash -c "my_function -args"si vous avez besoin d'exécuter une fonction au lieu d'un script
Anonyme
28
script -c <PROGRAM> -f OUTPUT.txt

La clé est -f. Citation du script man:

-f, --flush
     Flush output after each write.  This is nice for telecooperation: one person
     does 'mkfifo foo; script -f foo', and another can supervise real-time what is
     being done using 'cat foo'.

De fonctionner en arrière-plan:

nohup script -c <PROGRAM> -f OUTPUT.txt
user3258569
la source
Hou la la! Une solution qui fonctionne busybox! (Ma coquille gèle après, mais peu importe)
Victor Sergienko
9

Vous pouvez utiliser teepour écrire dans le fichier sans avoir besoin de vidage.

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null
crénelée
la source
2
Cela met toujours en mémoire tampon la sortie, du moins dans mon environnement Ubuntu 18.04. Le contenu finit par être écrit dans le fichier de toute façon, mais je pense que l'OP demande une méthode permettant de surveiller la progression plus précisément avant que le fichier ne finisse d'écrire, et cette méthode ne le permet pas plus que la redirection de sortie Est-ce que.
mltsy
3

Ce n'est pas une fonction de bash, car le shell ne fait qu'ouvrir le fichier en question, puis passer le descripteur de fichier comme sortie standard du script. Ce que vous devez faire est de vous assurer que la sortie de votre script est supprimée plus fréquemment que vous ne le faites actuellement.

En Perl par exemple, cela peut être accompli en définissant:

$| = 1;

Voir perlvar pour plus d'informations à ce sujet.

Greg Hewgill
la source
2

La mise en mémoire tampon de la sortie dépend de la manière dont votre programme /homedir/MyScriptest implémenté. Si vous trouvez que la sortie est mise en mémoire tampon, vous devez la forcer dans votre implémentation. Par exemple, utilisez sys.stdout.flush () s'il s'agit d'un programme python ou utilisez fflush (stdout) s'il s'agit d'un programme C.

Midas
la source
2

Cela aiderait-il?

tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

Cela affichera immédiatement les entrées uniques de access.log à l'aide de l' utilitaire stdbuf .

Ondra Žižka
la source
Le seul problème est que stdbuf semble être un ancien utilitaire qui n'est pas disponible sur les nouvelles distributions.
Ondra Žižka
..nor dans ma busybox :(
Campa
En fait, je suis stdbufdisponible dans Ubuntu pour le moment, je ne sais pas où je l'ai eu.
Ondra Žižka
J'ai stdbuf dans Centos 7.5
Max
1
Dans Ubuntu 18.04, stdbuffait partie de coreutilus(trouvé avec apt-file search /usr/bin/stdbuf).
Rmano
1

À quel point le problème vient d'être repéré ici , c'est que vous devez attendre que les programmes que vous exécutez à partir de votre script terminent leur travail.
Si dans votre script vous exécutez le programme en arrière-plan, vous pouvez essayer quelque chose de plus.

En général, un appel à syncavant de quitter permet de vider les tampons du système de fichiers et peut aider un peu.

Si dans le script vous démarrez certains programmes en arrière-plan ( &), vous pouvez attendre qu'ils se terminent avant de quitter le script. Pour avoir une idée de son fonctionnement, vous pouvez voir ci-dessous

#!/bin/bash
#... some stuffs ...
program_1 &          # here you start a program 1 in background
PID_PROGRAM_1=${!}   # here you remember its PID
#... some other stuffs ... 
program_2 &          # here you start a program 2 in background
wait ${!}            # You wait it finish not really useful here
#... some other stuffs ... 
daemon_1 &           # We will not wait it will finish
program_3 &          # here you start a program 1 in background
PID_PROGRAM_3=${!}   # here you remember its PID
#... last other stuffs ... 
sync
wait $PID_PROGRAM_1
wait $PID_PROGRAM_3  # program 2 is just ended
# ...

Puisque waitfonctionne avec les travaux ainsi qu'avec les PIDnombres, une solution paresseuse devrait être de mettre à la fin du script

for job in `jobs -p`
do
   wait $job 
done

Plus difficile est la situation si vous exécutez quelque chose qui exécute autre chose en arrière-plan car vous devez rechercher et attendre (si c'est le cas) la fin de tout le processus enfant : par exemple, si vous exécutez un démon, ce n'est probablement pas le cas attendre qu'il se termine :-).

Remarque:

  • wait $ {!} signifie "attendre que le dernier processus d'arrière-plan soit terminé" où $!est le PID du dernier processus d'arrière-plan. Donc mettre wait ${!}juste après program_2 &équivaut à exécuter directement program_2sans l'envoyer en tâche de fond avec&

  • De l'aide de wait:

    Syntax    
        wait [n ...]
    Key  
        n A process ID or a job specification
    
Hastur
la source
1

Merci @user3258569, le script est peut-être la seule chose qui fonctionne busybox!

La coquille a gelé pour moi après cela, cependant. En cherchant la cause, j'ai trouvé ces gros avertissements rouges "ne pas utiliser dans un shell non interactif" dans la page de manuel du script :

scriptest principalement conçu pour les sessions de terminal interactives. Lorsque stdin n'est pas un terminal (par exemple :) echo foo | script, la session peut se bloquer, car le shell interactif dans la session de script manque EOF et scriptn'a aucune idée de la fermeture de la session. Voir la section NOTES pour plus d'informations.

Vrai. script -c "make_hay" -f /dev/null | grep "needle"gelait la coquille pour moi.

Contrairement à l'avertissement, j'ai pensé que ça echo "make_hay" | scriptpassera un EOF, alors j'ai essayé

echo "make_hay; exit" | script -f /dev/null | grep 'needle'

et ça a marché!

Notez les avertissements dans la page de manuel. Cela peut ne pas fonctionner pour vous.

Victor Sergienko
la source
0

alternative à stdbuf est que awk '{print} END {fflush()}' je souhaite qu'il y ait un bash intégré pour faire cela. Normalement, cela ne devrait pas être nécessaire, mais avec les anciennes versions, il peut y avoir des bogues de synchronisation bash sur les descripteurs de fichiers.

Brian Chrisman
la source
-2

Je ne sais pas si cela fonctionnerait, mais qu'en est-il d'appeler sync?

fourchette et attendre
la source
1
syncest une opération de système de fichiers de bas niveau et n'a aucun rapport avec la sortie en mémoire tampon au niveau de l'application.
Greg Hewgill
2
syncécrit les tampons des systèmes de fichiers sales sur le stockage physique, si nécessaire. Ceci est interne au système d'exploitation; les applications exécutées sur le système d'exploitation voient toujours une vue cohérente du système de fichiers, que les blocs de disque aient été écrits ou non sur le stockage physique. Pour la question d'origine, l'application (script) met probablement en mémoire tampon la sortie dans un tampon interne à l'application, et le système d'exploitation ne saura même pas (encore) que la sortie est en fait destinée à être écrite sur stdout. Ainsi, une opération hypothétique de type "sync" ne pourrait pas "accéder" au script et extraire les données.
Greg Hewgill
-2

J'ai eu ce problème avec un processus d'arrière-plan sous Mac OS X utilisant le StartupItems. Voici comment je le résous:

Si je fais sudo ps auxje peux voir que mytoolc'est lancé.

J'ai trouvé que (en raison de la mise en mémoire tampon) lorsque Mac OS X s'arrête mytoolne transfère jamais la sortie à la sedcommande. Cependant, si j'exécute sudo killall mytool, mytooltransfère la sortie vers la sedcommande. Par conséquent, j'ai ajouté un stopcas au StartupItemsqui est exécuté lorsque Mac OS X s'arrête:

start)
    if [ -x /sw/sbin/mytool ]; then
      # run the daemon
      ConsoleMessage "Starting mytool"
      (mytool | sed .... >> myfile.txt) & 
    fi
    ;;
stop)
    ConsoleMessage "Killing mytool"
    killall mytool
    ;;
Homme libre
la source
Ce n'est vraiment pas une bonne réponse Freeman car elle est très spécifique à votre environnement. L'OP veut surveiller la sortie et non la tuer.
Gris
-3

bien que ça plaise ou non c'est ainsi que fonctionne la redirection.

Dans votre cas, la sortie (ce qui signifie que votre script est terminé) de votre script est redirigée vers ce fichier.

Ce que vous voulez faire, c'est ajouter ces redirections dans votre script.


la source