Chiedi allo script Bash di attendere il messaggio di stato prima di continuare


10

Sto accendendo il server Selenium con uno script bash e come puoi vedere dai timestamp nel registro qui sotto, ci vogliono circa 32 secondi per rendere la cosa completamente online:

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

Invece di utilizzare un comando "sleep 32" dopo aver avviato il server (per ritardare lo script prima di andare avanti), vorrei che il mio script bash attendesse fino a quando non vede la stringa "Started SocketListener", e quindi continua. È possibile?

Risposte:


8

È possibile utilizzare tail -fper continuare a leggere dal file man mano che cresce. Fai attenzione a ciò che ti nutre tail -f. È possibile reindirizzare tail -fin un filtro che attende fino alla chiusura della riga di registro desiderata. Ciò che non funzionerà è se si esegue il pipe tail -fin un filtro che esegue il pipe in un altro filtro, poiché il filtro intermedio bufferizzerà il suo output. Questo funziona:

: >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

Nota che ho messo tailin background. Questo perché quando sedtrova la linea desiderata, esce, ma la pipeline continua a funzionare finché tailattende la linea successiva, che potrebbe non venire immediatamente se non del tutto¹. tailuscirà quando arriverà la riga successiva e riceverà a SIGPIPE. Ciò può lasciare un tailprocesso vagante se il registro viene rimosso senza che venga scritta alcuna riga (ottenere il PID del tailprocesso per ucciderlo quando le seduscite sarebbero possibili ma complicate).

¹ Grazie a Peter.O per aver segnalato il bug in una versione precedente.


Una nota (pedanteria, forse) - Non conosco nessuna shell che effettivamente richiede un noop per consentire la scrittura di un file vuoto.
Chris Down

@ChrisDown Nemmeno io. Ma trovo più chiaro scrivere esplicitamente la no-op.
Gilles 'SO- smetti di essere malvagio'

Bello (+1) ... parlando di buffer: non capisco le macchinazioni di buffering, ma ha un problema da tenere presente, se la linea di destinazione non è immediatamente seguita da più linee di registro. Si blocca fino a quando non vengono scritte più righe, anche se sed ha già abbinato il modello (<- Non capisco quella parte). Un sedcomando modificato che scrive abbastanza nel log per forzare un flush (?), Lo avvia immediatamente (l'ho testato), ma penso che abbia la possibilità che i dati inseriti siano intervallati da una riga di selenio. .
Peter.O

@ Peter.O IIRC alcune implementazioni di sed leggono in anticipo la riga successiva, perché ci sono casi in cui è utile (l' $indirizzo, per esempio). Non ho visto che ciò accadesse nei miei test (con GNU sed). Come hai esposto esattamente il bug su quale sistema operativo?
Gilles 'SO- smetti di essere malvagio'

@Gilles: paste.ubuntu.com/863326 .. e: GNU sed versione 4.2.1 , tail (GNU coreutils) 7.4
Peter.O

3

È un po 'più difficile nello script della shell diretta, ma questo è quello che stavo usando da un po' di tempo per Tomcat e 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

Il alarmgestirà qualsiasi potenziale appeso in cui Tomcat fallito. Il numero di righe per tornare indietro dall'EOF è regolabile (da un file di configurazione).

Alla fine ho spostato l'intera cosa su Python; mentre è un po 'più lungo, è un po' più efficiente:

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()

1

Puoi aggiungere questo al tuo script per implementare la pausa:

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

Utilizza il modulo perl File :: Tail per comportarsi come tail -f logfile | grep Started SocketListener.

Sostituire / var / log / message con il file di registro appropriato. Si noti che si bloccherà per sempre se "Started SocketListener" non appare mai.


1

Forse dovresti usare un timeout invece di aspettare indefinitamente.

La funzione bash di seguito verrà bloccata fino a quando non viene visualizzato il termine di ricerca specificato o viene raggiunto un determinato timeout.

Lo stato di uscita sarà 0 se la stringa viene trovata entro il timeout.

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
}

Forse il file di registro non esiste ancora subito dopo l'avvio di Selenium. In tal caso, dovresti attendere che appaia prima di cercare la stringa:

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))
}

Ecco come puoi usarlo:

wait_selenium_server "/var/log/selenium.log" 5m && \
echo -e "\n-------------------------- Selenium READY --------------------------\n"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.