J'ai un journal de serveur qui produit une ligne de texte spécifique dans son fichier journal lorsque le serveur est en marche. Je veux exécuter une commande une fois que le serveur est en place, et donc faire quelque chose comme ceci:
tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?
Quelle est la meilleure façon de procéder?
Un moyen simple serait génial.
tail -f /path/to/serverLog | awk '
/Printer is on fire!/ { system("shutdown -h now") }
/new USB high speed/ { system("echo \"New USB\" | mail admin") }'
Et oui, ces deux messages sont de vrais messages provenant d'un journal du noyau. Perl pourrait être un peu plus élégant à utiliser pour cela et peut également remplacer le besoin de queue. Si vous utilisez Perl, cela ressemblera à ceci:
open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
if(eof $fd) {
sleep 1;
$fd->clearerr;
next;
}
my $line = <$fd>;
chomp($line);
if($line =~ /Printer is on fire!/) {
system("shutdown -h now");
} elsif($line =~ /new USB high speed/) {
system("echo \"New USB\" | mail admin");
}
}
Si vous ne recherchez qu'une seule possibilité et que vous souhaitez rester principalement dans le shell plutôt que d'utiliser awk
ou Perl
, vous pouvez faire quelque chose comme:
tail -F /path/to/serverLog |
grep --line-buffered 'server is up' |
while read ; do my_command ; done
... qui s'exécutera my_command
chaque fois que "le serveur est en place" apparaît dans le fichier journal. Pour plusieurs possibilités, vous pouvez peut-être supprimer le grep
et utiliser à la place un case
dans le while
.
La majuscule -F
Indique à tail
de surveiller la rotation du fichier journal; c'est-à-dire si le fichier actuel est renommé et qu'un autre fichier du même nom prend sa place, tail
basculera vers le nouveau fichier.
L'option --line-buffered
Indique à grep
de vider son tampon après chaque ligne; sinon, my_command
risque de ne pas être atteint en temps opportun (en supposant que les journaux contiennent des lignes de taille raisonnable).
Cette question semble déjà avoir reçu une réponse, mais je pense qu'il existe une meilleure solution.
Plutôt que tail | whatever
, Je pense que ce que vous voulez vraiment, c'est swatch
. Swatch est un programme conçu explicitement pour faire ce que vous demandez, regarder un fichier journal et exécuter des actions basées sur des lignes de journal. En utilisant tail|foo
exigera que vous ayez un terminal en cours d'exécution pour ce faire. Swatch d'autre part fonctionne comme un démon et surveillera toujours vos journaux. Swatch est disponible dans toutes les distributions Linux,
Je vous encourage à l'essayer. Bien que vous puissiez enfoncer un clou avec l'arrière d'un tournevis, cela ne signifie pas que vous devriez le faire.
Le meilleur tutoriel de 30 secondes sur swatch que j'ai pu trouver est ici .
Il est étrange que personne n'ait mentionné l'utilitaire multitail
qui a cette fonctionnalité prête à l'emploi. Un exemple d'utilisation:
Afficher la sortie d'une commande ping et si elle affiche un délai d'expiration, envoyer un message à tous les utilisateurs actuellement connectés
multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"
Voir aussi autres exemples d'utilisation multitail
.
Voyons à quel point cela pourrait être simple et lisible:
mylog() {
echo >>/path/to/myscriptLog "$@"
}
while read line;do
case "$line" in
*"Printer on fire"* )
mylog Halting immediately
shutdown -h now
;;
*DHCPREQUEST* )
[[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\ ]]
mylog Incomming or refresh for ${BASH_REMATCH[1]}
$HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
;;
* )
mylog "untrapped entry: $line"
;;
esac
done < <(tail -f /path/to/logfile)
Bien que vous n'utilisiez pas le regex
de bash, cela pourrait rester très rapide!
Mais pour les serveurs à forte charge, et comme j'aime sed
parce que c'est très rapide et très évolutif, j'utilise souvent ceci:
while read event target lost ; do
case $event in
NEW )
ip2int $target intTarget
((count[intTarget]++))
...
esac
done < <(tail -f /path/logfile | sed -une '
s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
...
')
C'est comme ça que j'ai commencé à faire ça aussi, mais je suis devenu beaucoup plus sophistiqué avec ça. Quelques points à considérer:
J'utilise quelque chose dans le sens de ceci:
RELEASE=/tmp/${RANDOM}$$
(
trap 'false' 1
trap "rm -f ${RELEASE}" 0
while ! [ -s ${RELEASE} ]; do sleep 3; done
# You can put code here if you want to do something
# once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}
Cela fonctionne en maintenant tail
ouvert jusqu'à ce que ${RELEASE}
le fichier contient des données.
Une fois que grep
réussit:
${RELEASE}
Qui va${wait_pid}
processus pourtail
Remarque: sed
peut être plus sophistiqué pour déterminer réellement le nombre de lignes que tail
produira au démarrage et supprimer ce nombre. Mais en général, c'est 10.