web-dev-qa-db-fra.com

Forcer le vidage de la sortie dans un fichier pendant l'exécution du script bash

J'ai un petit script, appelé quotidiennement par crontab à l'aide de la commande suivante:

/homedir/MyScript &> some_log.log

Le problème avec cette méthode est que some_log.log n'est créé qu'après la fin de MyScript. Je voudrais vider la sortie du programme dans le fichier pendant son exécution pour pouvoir faire des choses comme 

tail -f some_log.log

et suivre les progrès, etc.

59
olamundo

bash lui-même n’écrira jamais de sortie dans votre fichier journal. Au lieu de cela, les commandes qu'il appelle dans le cadre du script écriront et afficheront individuellement la sortie à chaque fois qu'elles le voudront. Votre question est donc de savoir comment forcer le vidage des commandes du script bash, et cela dépend de ce qu'elles sont.

25
Chris Dodd

J'ai trouvé une solution à ceci 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 Nohup pour exécuter des travaux longs sur une machine distante.

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

Ainsi, votre processus ne sera pas annulé lorsque vous vous déconnecterez.

57
Martin Wiebusch

script -c <PROGRAM> -f OUTPUT.txt

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

 -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
18
user3258569

Vous pouvez utiliser tee pour écrire dans le fichier sans vidage.

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null
8
crenate

Ce n'est pas une fonction de bash, car tout ce que le shell fait est d'ouvrir le fichier en question puis de transmettre le descripteur de fichier comme sortie standard du script. Ce que vous devez faire, c'est vous assurer que la sortie est vidée de votre script plus souvent que vous ne l'êtes actuellement.

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

$| = 1;

Voir perlvar pour plus d'informations à ce sujet.

3
Greg Hewgill

Est-ce que cela aiderait?

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

Ceci affichera immédiatement les entrées uniques de access.log
http://www.pixelbeat.org/programming/stdio_buffering/stdbuf-man.html

2
Ondra Žižka

La mise en mémoire tampon de la sortie dépend de la manière dont votre programme /homedir/MyScript est implémenté. Si vous constatez 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 fflush (stdout) s’il s’agit d’un programme C.

2
Midas

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

Le Shell a gelé pour moi après. En cherchant la cause, j'ai trouvé ces gros avertissements rouges "ne pas utiliser dans des shells non interactifs" dans page de manuel de script

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

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

Pays à l'avertissement, je pensais que echo "make_hay" | script passera un EOF, alors j'ai essayé

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

et ça a fonctionné!

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

1
Victor Sergienko

Comment vient de voir ici le problème est que vous devez attendre que les programmes que vous exécutez à partir de votre script finissent leur travail.
Si, dans votre script, vous exécutez un programme dans background, vous pouvez essayer quelque chose de plus. 

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

Si, dans le script, vous démarrez des programmes dans background (&), vous pouvez wait qu'ils se terminent avant de quitter le script. Pour avoir une idée de son fonctionnement, voyez 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
# ...

Étant donné que wait fonctionne avec des travaux aussi bien qu'avec des nombres PID, une solution peu fiable devrait être de mettre à la fin du script 

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

La situation est plus difficile 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 tous les processus child: par exemple, si vous exécutez un démon ce n'est probablement pas le cas d'attendre que ça 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_2 sans l'envoyer en arrière-plan avec &

  • De l'aide de wait

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

l'alternative à stdbuf est awk '{print} END {fflush()}' J'aimerais qu'il y ait une bash intégrée pour le faire . Normalement, cela ne devrait pas être nécessaire, mais avec les anciennes versions, il pourrait y avoir des bugs de synchronisation bash sur les descripteurs de fichiers.

0
Brian Chrisman