Demandez au script Bash d'attendre le message d'état avant de continuer

10

Je lance le serveur Selenium avec un script bash et comme vous pouvez le voir sur les horodatages du journal ci-dessous, il faut environ 32 secondes pour que la chose soit entièrement en ligne:

Feb 28, 2012 10:19:02 PM org.openqa.grid.selenium.GridLauncher main
INFO: Launching a standalone server
22:19:02.835 INFO - Java: Sun Microsystems Inc. 20.0-b11
22:19:02.836 INFO - OS: Linux 2.6.32-220.4.1.el6.x86_64 amd64
22:19:02.852 INFO - v2.19.0, with Core v2.19.0. Built from revision 15849
22:19:02.988 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub
22:19:02.990 INFO - Version Jetty/5.1.x
22:19:02.992 INFO - Started HttpContext[/selenium-server/driver,/selenium-server/driver]
22:19:02.993 INFO - Started HttpContext[/selenium-server,/selenium-server]
22:19:02.993 INFO - Started HttpContext[/,/]
22:19:34.552 INFO - Started org.openqa.jetty.jetty.servlet.ServletHandler@488e32e7
22:19:34.552 INFO - Started HttpContext[/wd,/wd]
22:19:34.555 INFO - Started SocketListener on 0.0.0.0:4444
22:19:34.555 INFO - Started org.openqa.jetty.jetty.Server@7d29f3b5

Au lieu d'utiliser une commande "sleep 32" après le démarrage du serveur (pour retarder le script avant de continuer), j'aimerais que mon script bash attende qu'il voit la chaîne "Started SocketListener", puis continue. Est-ce possible?

Eric
la source

Réponses:

8

Vous pouvez utiliser tail -fpour continuer à lire le fichier à mesure qu'il grandit. Soyez prudent avec ce que vous nourrissez tail -f. Vous pouvez diriger tail -fvers un filtre qui attend la ligne de journal souhaitée et se ferme. Ce qui ne fonctionnera pas, c'est si vous redirigez tail -fvers un filtre qui redirige vers un autre filtre, car le filtre intermédiaire mettra sa sortie en mémoire tampon. Cela marche:

: >file.log  # create an empty log file
start-selenium-session --log-file=file.log &
{ tail -n +1 -f file.log & } | sed -n '/Started SocketListener/q'
speak-to-socket

A noter que je mets tailen fond d'écran. En effet, lorsqu'il sedtrouve la ligne souhaitée, il se ferme, mais le pipeline continue de fonctionner aussi longtemps qu'il tailattend la ligne suivante, qui peut ne pas arriver immédiatement, voire pas du tout¹. tailquittera lorsque la ligne suivante arrivera et qu'il recevra un SIGPIPE. Cela peut laisser un tailprocessus erroné si le journal est supprimé sans qu'aucune ligne ne lui soit écrite (obtenir le PID du tailprocessus pour le tuer lorsque les sedsorties seraient possibles mais difficiles).

¹ Merci à Peter.O d' avoir signalé le bug dans une version antérieure.

Gilles 'SO- arrête d'être méchant'
la source
Une note (pédanterie, peut-être) - Je ne connais aucun shell qui nécessite en fait un noop pour permettre l'écriture d'un fichier vide.
Chris Down
@ChrisDown Moi non plus. Mais je trouve plus clair d'écrire explicitement le no-op.
Gilles 'SO- arrête d'être méchant'
Nice (+1) ... en parlant de tampons: je ne comprends pas les machinations de tamponnage, mais il y a un problème à savoir, si la ligne cible n'est pas immédiatement suivie par plus de lignes de journal. Il se bloque jusqu'à ce que plus de lignes soient écrites, même si sed a déjà correspondu au motif (<- je ne comprends pas cette partie). Une sedcommande modifiée qui écrit suffisamment dans le journal pour forcer un vidage (?), La lance immédiatement (je l'ai testée), mais je pense que cela a la possibilité que les données insérées soient entrecoupées d'une ligne de session au sélénium. .
Peter.O
@ Peter.O IIRC certaines implémentations de sed lisent la ligne suivante à l'avance, car il y a des cas où c'est utile (l' $adresse, par exemple). Je n'ai pas vu cela se produire dans mes tests (avec GNU sed). Comment avez-vous exposé exactement le bug, sur quel système d'exploitation?
Gilles 'SO- arrête d'être méchant'
@Gilles: paste.ubuntu.com/863326 .. et: GNU sed version 4.2.1 , tail (GNU coreutils) 7.4
Peter.O
3

C'est un peu plus difficile dans un script shell direct, mais c'est ce que j'utilisais depuis un certain temps pour tomcat et oc4j:

perlscr='
alarm 120;
open F, "<$ARGV[0]";
seek F -($ARGV[1]*80),2;
while (1) {exit if (<F>=~$ARGV[2]);}'

window=10
scanfor="^INFO: Server startup in \d+ ms"
perl -e "$perlscr" $logfile $window "$scanfor" 2>&1 0<&1

Le alarmgérera tout accrochage potentiel où Tomcat a échoué. Le nombre de lignes à partir de l'EOF est réglable (à partir d'un fichier de configuration).

J'ai finalement déplacé le tout en python; alors qu'il est un peu plus long, il est un peu plus efficace:

class Alarm:
    import signal
    signal_signal = signal.signal
    signal_SIGALRM = signal.SIGALRM
    signal_SIG_DFL = signal.SIG_DFL
    del signal
    def __init__(self, delay)
       self.howlong = delay
       self.clear()
    def __del__(self):
       self.reset_signals()
    def __nonzero__(self):
       return self.state
    def clear(self):
       self.state = False
       self.reset_signals()
    def _start_alarm(self):
       from signal import alarm
       alarm(self.howlong)
    def _set_sigalarm(self, handler):
        if handler:
            self.signal_signal(self.signal_SIGALRM, handler)
        else:
            self.signal_signal(self.signal_SIGALRM, self.signal_SIG_DFL)
    def reset_signals(self):
        self._set_sigalarm(None)
    def set_signals(self):
        self._set_sigalarm(self.handler)
    def handler(self, signo, frame):
        self.state = False
    def start(self):
        self.state = True
        self.set_signals()
        self._start_alarm()
    def stop(self):
        self.reset_signals()
        self.state = False
found = False
scanfor = re.compile('^INFO: Server startup in \d+ ms')
window = 10
logfile = open(logfilename, 'r')
logfile.seek(window * 80, 2)
alarm = Alarm(timeout)
try:
    alarm.start()
    while alarm:
        line = logfile.readline()
        if line:
            m = scanfor.search(line)
            if m:
                alarm.stop()
                found = True
                break
        time.sleep(0.1)
finally:
    alarm.clear()
Arcege
la source
1

Vous pouvez l'ajouter à votre script pour implémenter la pause:

perl -e 'use File::Tail;
    my $ref=tie *FH,"File::Tail",(name=>"/var/log/messages",maxinterval=>1);
    while(<FH>) { exit if /Started SocketListener/ };'

Il utilise le module perl File :: Tail pour se comporter comme tail -f logfile | grep Started SocketListener.

Remplacez / var / log / message par le fichier journal approprié. Notez qu'il se bloquera indéfiniment si "Démarré SocketListener" n'apparaît jamais.

R Perrin
la source
1

Vous devriez peut-être utiliser un délai d'attente au lieu d'attendre indéfiniment.

La fonction bash ci-dessous se bloquera jusqu'à ce que le terme de recherche donné apparaisse ou qu'un délai donné soit atteint.

Le statut de sortie sera 0 si la chaîne est trouvée dans le délai.

wait_str() {
  local file="$1"; shift
  local search_term="$1"; shift
  local wait_time="${1:-5m}"; shift # 5 minutes as default timeout

  (timeout $wait_time tail -F -n0 "$file" &) | grep -q "$search_term" && return 0

  echo "Timeout of $wait_time reached. Unable to find '$search_term' in '$file'"
  return 1
}

Peut-être que le fichier journal n'existe pas encore juste après le lancement de Selenium. Dans ce cas, vous devez attendre qu'il apparaisse avant de rechercher la chaîne:

wait_selenium_server() {
  echo "Waiting for Selenium server..."
  local server_log="$1"; shift
  local wait_time="$1"; shift

  wait_file "$server_log" 10 || { echo "Selenium log file missing: '$server_log'"; return 1; }

  wait_str "$server_log" "Started SocketListener" "$wait_time"
}

wait_file() {
  local file="$1"; shift
  local wait_seconds="${1:-10}"; shift # 10 seconds as default timeout

  until test $((wait_seconds--)) -eq 0 -o -f "$file" ; do sleep 1; done

  ((++wait_seconds))
}

Voici comment vous pouvez l'utiliser:

wait_selenium_server "/var/log/selenium.log" 5m && \
echo -e "\n-------------------------- Selenium READY --------------------------\n"
Elifarley
la source