Haga que la secuencia de comandos Bash espere el mensaje de estado antes de continuar

10

Estoy iniciando el servidor Selenium con un script bash y, como puede ver en las marcas de tiempo en el registro a continuación, la cosa tarda unos 32 segundos en conectarse por completo:

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

En lugar de utilizar un comando "sleep 32" después de iniciar el servidor (para retrasar el script antes de continuar), me gustaría que mi script bash espere hasta que vea la cadena "Started SocketListener" y luego continúe. ¿Es eso posible?

Eric
fuente

Respuestas:

8

Puede usar tail -fpara seguir leyendo el archivo a medida que crece. Tenga cuidado con lo que alimenta tail -f. Puede canalizar tail -fen un filtro que espera hasta la línea de registro deseada y se cierra. Lo que no funcionará es si se conecta tail -fa un filtro que se conecta a otro filtro, porque el filtro intermedio amortiguará su salida. Esto funciona:

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

Tenga en cuenta que lo puse tailen el fondo. Esto se debe a que cuando sedencuentra la línea deseada, sale, pero la tubería continúa ejecutándose mientras tailespera la siguiente línea, que puede no aparecer de inmediato immediately. tailsaldrá cuando llegue la siguiente línea y reciba a SIGPIPE. Esto puede dejar un tailproceso perdido si el registro se elimina sin que se escriba ninguna línea en él (obtener el PID del tailproceso para eliminarlo cuando las sedsalidas serían posibles pero difíciles).

¹ Gracias a Peter.O por señalar el error en una versión anterior.

Gilles 'SO- deja de ser malvado'
fuente
Una nota (pedantería, tal vez): no conozco ningún shells que realmente requiera un noop para permitir escribir un archivo vacío.
Chris Down
@ChrisDown Yo tampoco. Pero me parece más claro escribir explícitamente el no-op.
Gilles 'SO- deja de ser malvado'
Agradable (+1) ... hablando de buffers: no entiendo las maquinaciones de buffering, pero tiene un problema a tener en cuenta, si la línea de destino no es seguida inmediatamente por más líneas de registro. Se cuelga hasta que se escriben más líneas, a pesar de que sed ya ha coincidido con el patrón (<- No entiendo esa parte). Un sedcomando modificado que escribe lo suficiente en el registro para forzar un vaciado (?) Lo activa inmediatamente (lo probé), pero creo que tiene la posibilidad de que los datos insertados se intercalen con una línea de sesión de selenio. .
Peter.O
@ Peter.O IIRC algunas implementaciones de sed leen la siguiente línea de antemano, porque hay casos en los que es útil (la $dirección, por ejemplo). Sin embargo, no vi que eso sucediera en mis pruebas (con GNU sed). ¿Cómo exhibiste exactamente el error, en qué sistema operativo?
Gilles 'SO- deja de ser malvado'
@Gilles: paste.ubuntu.com/863326 .. y: GNU sed versión 4.2.1 , tail (GNU coreutils) 7.4
Peter.O
3

Es un poco más difícil en el script de shell directo, pero esto es lo que estaba usando durante bastante tiempo para tomcat y 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

El alarmmanejará cualquier posible bloqueo donde Tomcat falló. El número de líneas para regresar desde el EOF es ajustable (desde un archivo de configuración).

Eventualmente moví todo a Python; Si bien es un poco más largo, es un poco más eficiente:

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
fuente
1

Puede agregar esto a su script para implementar 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/ };'

Utiliza el módulo perl File :: Tail para comportarse como tail -f logfile | grep Started SocketListener.

Reemplace / var / log / message con el archivo de registro apropiado. Tenga en cuenta que se bloqueará para siempre si "Started SocketListener" nunca aparece.

R Perrin
fuente
1

Tal vez deberías usar un tiempo de espera en lugar de esperar indefinidamente.

La función bash a continuación se bloqueará hasta que aparezca el término de búsqueda dado o se alcance un tiempo de espera determinado.

El estado de salida será 0 si la cadena se encuentra dentro del tiempo de espera.

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
}

Quizás el archivo de registro aún no exista justo después de iniciar Selenium. En ese caso, debe esperar a que aparezca antes de buscar la cadena:

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

Así es como puedes usarlo:

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