J'ai le suivant.
tail -f
pour le message de réussite.Même si j'ai l'exit 0 dans le code, je ne peux pas terminer le processus tail -f
.
Ce qui ne laisse pas mon script se terminer. Y at-il une autre façon de faire cela dans Bash?
Le code ressemble à ce qui suit.
function startServer() {
touch logfile
startJavaprocess > logfile &
tail -f logfile | while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
exit 0
fi
done
}
La meilleure réponse que je puisse trouver est la suivante:
tail -f logfile | read -t 30 line
--pid=$$
, ainsi il se fermera à la fin du processus bash.Je couvrirai tous les cas auxquels je peux penser (le serveur se bloque sans sortie, les sorties du serveur, le serveur démarre correctement).
N'oubliez pas de commencer votre queue avant le serveur.
tail -n0 -F logfile 2>/dev/null | while read -t 30 line
le -F
"lira" le fichier même s'il n'existe pas (commencez à le lire quand il apparaît). Le -n0
ne lit rien dans le fichier, vous pouvez donc continuer à ajouter au fichier journal au lieu de l'écraser à chaque fois, ainsi qu'à la rotation standard du journal.
MODIFIER:
Ok, donc une "solution" assez grossière, si vous utilisez tail. Il y a probablement de meilleures solutions utilisant autre chose que la queue, mais je dois vous la donner, la queue vous sort du tuyau cassé assez bien. Un «tee» capable de gérer SIGPIPE fonctionnerait probablement mieux. Le processus Java effectuant activement une suppression de système de fichiers avec un message 'im alive' est probablement encore plus facile à attendre.
function startServer() {
touch logfile
# 30 second timeout.
sleep 30 &
timerPid=$!
tail -n0 -F --pid=$timerPid logfile | while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
# stop the timer..
kill $timerPid
fi
done &
startJavaprocess > logfile &
# wait for the timer to expire (or be killed)
wait %sleep
}
D'après les réponses que j'ai trouvées ici, voici ce que j'ai proposé.
Il traite directement avec la queue et le tue une fois que nous avons vu la sortie de journal nécessaire. L'utilisation de 'pkill -P $$ tail' devrait permettre de tuer le bon processus.
wait_until_started() {
echo Waiting until server is started
regex='Started'
tail logfile -n0 -F | while read line; do
if [[ $line =~ $regex ]]; then
pkill -9 -P $$ tail
fi
done
echo Server is started
}
Selon la page de manuel tail , vous pouvez obtenir la fin de tail après le processus
Dans BASH, vous pouvez obtenir le PID du dernier processus d'arrière-plan démarré à l'aide de $! SO si vous utilisez bash:
tail -f --pid=$! logfile
J'ai eu une situation similaire dans laquelle je devais rédiger un journal pour un message "démarré" dans un délai raisonnable et s'il n'était pas trouvé pendant cette période, je devais quitter. Voici ce que j'ai fini par faire.
wait_Tomcat_start(){
WAIT=60
echo "Waiting for Tomcat to initialize for $WAIT seconds"
# Tail log file, do a while read loop with a timeout that checks for desired log status,
# if found kill the find and break the loop. If not found within timeout: the read -t will
# kill the while read loop and bounce to the OR statement that will in turn kill the tail
# and echo some message to the console.
tail -n0 -f $SERVERLOG | while read -t $WAIT LINE || (pkill -f "tail -n0 -f" && echo "Tomcat did not start in a timely fashion! Please check status of Tomcat!!!")
do
echo "$LINE"
[[ "${LINE}" == *"Server startup in"* ]] && pkill -f "tail -n0 -f" && break
done
}
Je ne suis pas sûr que ce soit très élégant ni même le meilleur moyen de le faire, mais cela fonctionne assez bien pour moi. Je serais heureux pour toutes les opinions :)
Capturer le pid du processus d'arrière-plan
pid=$!
Utilisez l'option --pid = PID de tail pour qu'elle se termine après la fin du processus dont le pid $ PID est terminé.
Il est possible d'arrière-plan tail -f logfile
, d'envoyer tailpid
au sous-shell de boucle while read
et d'implémenter une trap
sur EXIT pour supprimer la commande tail
.
( (sleep 1; exec tail -f logfile) & echo $! ; wait) | (
trap 'trap - EXIT; kill "$tailpid"; exit' EXIT
tailpid="$(head -1)"
while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
exit 0
fi
done
)
J'ai eu le même problème, impossible de trouver une solution simple et bonne. Je ne suis pas bon en Python, mais j'ai réussi à résoudre ceci:
wait_log.py :
#!/usr/bin/env python
from optparse import OptionParser
import os
import subprocess
import time
def follow(file):
def file_size(file):
return os.fstat(file.fileno())[6]
def is_newLine(line):
return line != None and line.find("\n") != -1;
file.seek(0, os.SEEK_END)
while True:
if file.tell() > file_size(file):
file.seek(0, os.SEEK_END)
line_start = file.tell()
line = file.readline()
if is_newLine(line):
yield line
else:
time.sleep(0.5)
file.seek(line_start)
def wait(file_path, message):
with open(file_path) as file:
for line in follow(file):
if line.find(message) != -1:
break
def main():
parser = OptionParser(description="Wait for a specific message in log file.", usage="%prog [options] message")
parser.add_option("-f", "--file", help="log file")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("message not provided")
if options.file == None:
parser.error("file not provided")
wait(options.file, args[0])
if __== "__main__":
main()
Plutôt que de quitter le processus, vous pouvez plutôt trouver l'ID de processus du processus tail -f
et le tuer (un kill -9
serait même sans danger ici si vous êtes certain que le fichier journal est terminé).
De cette façon, le while read line
se terminera naturellement et vous n'aurez pas besoin de quitter.
Ou, puisque vous n'utilisez pas vraiment la variable tail
pour la sortie à l'écran, vous pouvez également essayer la version plus ancienne:
grep -q 'Started' logfile
while [[ $? -ne 0 ]] ; do
sleep 1
grep -q 'Started' logfile
done
Avait un problème similaire où le processus de queue n'a pas été tué quand
Utilisez le --pid=$!
pour le tuer et démarrez une boucle while infinie pour faire écho à quelque chose en arrière-plan avant la queue qui est tuée lorsque le processus sous-jacent est tué et tue donc la queue.
( while true; do echo 'running'; sleep 5; done ) & ( tail -f --pid=$! log-file )
Pourquoi ne pas utiliser une boucle infinie au lieu de l'option de ligne de commande -f pour tail?
function startServer() {
startJavaprocess > logfile &
while [ 1 ]
do
if tail logfile | grep -q 'Started'; then
echo 'Server started'
exit 0
fi
done
}
Utiliser tail -n0 -f canalisé vers grep est en effet une solution intéressante, et le premier processus du pipeline mourra lorsqu'il tentera de générer un processus grep mort.
Mais si vous recherchez un texte proche de la dernière sortie courante de la queue, grep aura déjà lu l'intégralité de l'entrée dans la queue (dans un bloc) et il n'y aura donc plus de sortie de texte dans la liste. log qui doit être envoyé dans le tuyau car grep le lisait déjà avant de quitter (ou peut-être était-il déjà dans le tampon du tuyau) - du moins, c'est ce que je comprends.
Utiliser l'option "-m1" sur grep donne l'impression que cela ferait exactement ce que vous souhaitiez et laisserait l'entrée juste après la ligne correspondante, mais cela ne semblait pas faire une différence ni m'aider dans ma recherche de fonctionnalités similaires. Je soupçonne que le tampon de canal contient toujours tout le texte produit par tail, ou une autre raison pour que tail n'ait plus rien à sortir. Vous vouliez que ce texte post-grep-match soit encore affiché, parce que c'est ce qui tuerait votre queue quand il essaierait (toujours risqué - que se passe-t-il si c'est la dernière ligne pour une raison quelconque?), Et rendre le contrôle au script appelant .
J'ai trouvé un moyen de ne rien afficher à la fin du fichier journal une fois que le grep s'est arrêté; c'est à dire.
tail -f logfile | (grep -q; echo >> fichier journal)
J'ai une théorie selon laquelle (si ma supposition est correcte), vous pouvez forcer le canal à être moins protégé pour que cela fonctionne correctement, ou peut-être que l'ajout d'une commande de définition huponexit au composant de canal approprié - c'est-à-dire entre crochets (probablement bouclés) Aidez-moi; mais je me fichais de l'ajout d'une ligne vierge au fichier journal et tout fonctionnait correctement. Il ne s'agissait que d'un script de test plus petit (il ne s'agit donc pas d'un fichier journal long qui doit rester dans un format de traitement différent).
shopt -s huponexit ne serait utile que pour la subsistance de celui-ci.
PS, mon premier post ici, aimerait le faire comme commentaire de réponse existante plutôt que de répéter, mais je ne pense pas pouvoir le faire maintenant.
Cela devrait fonctionner et la queue devrait mourir une fois que le sous-shell est mort
function startServer() {
touch logfile
startJavaprocess > logfile &
while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
exit 0
fi
done < <(tail -f logfile)
}
Try this:
function startServer() { while read line do if echo $line | grep -q 'Started'; then echo 'Server Started' return 0 fi done < <(startJavaprocess | tee logfile) }
Pour la question initiale, pourquoi la commande de sortie n'a pas quitté, j'ai eu le même problème et finalement trouvé la cause.
En utilisant le mode de débogage de bash, je peux voir que la commande exit a été appelée, mais le processus est toujours suspendu jusqu'à ce qu'une ligne supplémentaire vienne affleurer le fichier journal, juste après le 'Démarré'. Ce qui est bizarre, c’est quand «Started» est sorti, même si la sortie a été appelée, le processus est toujours lié à quelque chose. Je suppose que c’était tail -f
, jusqu’à ce qu’une autre ligne apparaisse, elle relâchera vraiment le crochet.
Donc, si vous imprimez une ligne de plus après son démarrage, votre moniteur s’arrête immédiatement.
tail -n0 --pid=$(($BASHPID+1)) -F logfile | sed -n '/Started/{s/.*/Server Started/p; q}'
Lors du transfert, les PID sont séquentiels, le pid de la queue sera donc $ BASHPID et le pid du fichier sed sera $ BASHPID + 1. Le commutateur --pid provoquera la fermeture de tail (correctement!) Lorsque la commande sed se fermera. Cette commande sed cherchera/Démarré/puis substituera toute la ligne (. *) À "Serveur démarré", puis quittera.
Exécuter la commande précédente avec Nohup.
Dans mon cas, exécutez Java -jar avec Nohup, tel que
Nohup Java -jar trade.jar xx.jar &
il n'y aura pas de journal de sortie, mais un nouveau "Nohup.out" sera créé. Le fichier journal original trade.log fonctionne également.
Ensuite, tail -f trade.log
, le shell affichera les informations du journal. Ctrl-c pourra l’interrompre et retourner à Shell.
Ma solution préférée pour ce problème consiste à placer la commande "tail" et son consommateur dans un sous-shell et à laisser la logique de filtrage tuer le parent et ses enfants (ce qui inclut le processus tail). Si vous regardez l'arbre de processus, ce sera:
startServer (pid=101)
startServer (pid=102) << This is the subshell created by using parens "(...)"
tail -f logfile (pid=103) << Here's the tail process
startServer (pid=104) << Here's the logic that detects the end-marker
Dans cette approche, la logique de détection de marqueur d'extrémité (pid 104) recherche son PID parent (102), ainsi que tous ses enfants, et tue l'ensemble du lot, y compris le sien. Ensuite, le grand-parent (pid 101 ci-dessus) est libre de continuer.
function startServer() {
touch logfile
startJavaprocess > logfile &
tail -f logfile | while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
mypid=$BASHPID
pipeParent=$(awk '/^PPid/ {print $2}' /proc/$mypid/status)
kill -TERM $pipeParent $(pgrep -P $pipeParent) # Kill the subshell and kids
fi
done
}
# To invoke startServer(), add a set of parens -- that puts it in a subshell:
(startServer())
À l'aide d'une combinaison de réponses, j'ai proposé cette solution simple. Cet exemple appelle le script startup.sh de Tomcat, puis décompresse le journal catalina.out
jusqu'à ce que "Démarrage du serveur" soit consigné, puis il cesse de se terminer.
#!/bin/bash
function logUntilStarted() {
tail -n0 -F /home/Tomcat/logs/catalina.out | while read line; do
if echo $line && echo $line | grep -q 'Server startup' ; then
pkill -9 -P $$ tail > /dev/null 2>&1
fi
done
}
/home/Tomcat/bin/startup.sh
logUntilStarted
N'utilisez pas tail - vous pouvez obtenir le même 'surveiller la dernière chose dans le fichier' en utilisant read
.
Ici, j'utilise un FIFO à la place du fichier journal:
function startServer() {
mkfifo logfile
startJavaprocess > logfile &
a=""; while [ "$a" != "Started" ]; do read <logfile a; done
echo "Server Started"
}
Notez que cela laisse un FIFO traîner.