Przed kontynuowaniem skrypt Bash musi czekać na komunikat o stanie

10

Odpalam serwer Selenium za pomocą skryptu bash i jak widać na podstawie poniższych znaczników czasu w dzienniku, przejście do trybu online zajmuje około 32 sekund:

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

Zamiast używać komendy „sleep 32” po uruchomieniu serwera (aby opóźnić skrypt przed przejściem dalej), chciałbym, aby mój skrypt bash czekał, aż zobaczy ciąg „Started SocketListener”, a następnie kontynuuje. Czy to jest możliwe?

Eric
źródło

Odpowiedzi:

8

Możesz użyć, tail -faby kontynuować czytanie z rosnącego pliku. Uważaj na to, w co żywisz tail -f. Możesz podłączyć tail -fdo filtra, który czeka na żądaną linię dziennika i kończy działanie. To, co nie zadziała, to jeśli potokujesz tail -fdo filtra, który potokuje do innego filtra, ponieważ filtr pośredni buforuje jego wynik. To działa:

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

Zauważ, że umieszczam tailw tle. Dzieje się tak, ponieważ gdy sedznajdzie żądaną linię, wychodzi, ale potok działa tak długo, jak tailczeka na następną linię, która może nie przyjść natychmiast, jeśli w ogóle¹. tailwyjdzie, gdy pojawi się następna linia i otrzyma SIGPIPE. Może to pozostawić zbłąkany tailproces, jeśli dziennik zostanie usunięty bez zapisywania do niego wiersza (uzyskanie PID tailprocesu, aby go zabić, gdy sedwyjście byłoby możliwe, ale trudne).

¹ Dziękujemy Peter.O za wskazanie błędu we wcześniejszej wersji.

Gilles „SO- przestań być zły”
źródło
Uwaga (być może pedanteria) - nie znam żadnych powłok, które wymagałyby noop, aby umożliwić zapisanie pustego pliku.
Chris Down
@ChrisDown Ja też nie. Ja uważam, że wyraźniej jest napisać no-op.
Gilles „SO - przestań być zły”
Fajnie (+1) ... mówiąc o buforach: nie rozumiem machinacji buforujących, ale trzeba pamiętać o jednym problemie, jeśli po linii docelowej nie następuje natychmiast więcej linii logów. Zawiesza się, dopóki nie zostanie napisanych więcej wierszy, mimo że sed już dopasował wzór (<- nie rozumiem tej części). Zmodyfikowane sedpolecenie, które zapisuje w dzienniku wystarczająco dużo, aby wymusić opróżnienie (?), Uruchamia go natychmiast (przetestowałem), ale myślę, że ma to możliwość, że wstawione dane mogą być przeplatane linią sesji selenu. .
Peter.O
@ Peter.O IIRC niektóre implementacje sed czytają wcześniej następny wiersz, ponieważ są przypadki, w których jest to przydatne ( $adres, na przykład). Nie widziałem tego jednak w moich testach (z GNU sed). Jak dokładnie pokazałeś błąd, na jakim systemie operacyjnym?
Gilles „SO- przestań być zły”
@Gilles: paste.ubuntu.com/863326 .. i: GNU sed wersja 4.2.1 , tail (GNU coreutils) 7.4
Peter.O
3

Jest to trochę trudniejsze w skrypcie prostej powłoki, ale tego właśnie używałem od dłuższego czasu dla tomcat i 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

alarmBędzie obsługiwać wszelkie potencjalne powieszenie gdzie Tomcat nie powiodło się. Liczba linii do powrotu z EOF jest regulowana (z pliku konfiguracyjnego).

W końcu przeniosłem całą rzecz na Python; choć jest nieco dłuższy, jest nieco bardziej wydajny:

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
źródło
1

Możesz dodać to do skryptu, aby wprowadzić pauzę:

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

Wykorzystuje moduł perla File :: Tail , aby zachowywać się jak tail -f logfile | grep Started SocketListener.

Zamień / var / log / message na odpowiedni plik dziennika. Pamiętaj, że zawiesi się na zawsze, jeśli „Started SocketListener” nigdy się nie pojawi.

R Perrin
źródło
1

Może powinieneś skorzystać z limitu czasu zamiast czekać w nieskończoność.

Poniższa funkcja bash będzie blokować, dopóki nie pojawi się podany termin wyszukiwania lub nie upłynie określony limit czasu.

Status wyjścia wyniesie 0, jeśli ciąg zostanie znaleziony w ciągu limitu czasu.

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
}

Być może plik dziennika jeszcze nie istnieje tuż po uruchomieniu Selenium. W takim przypadku należy zaczekać na jego pojawienie się przed wyszukiwaniem ciągu:

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

Oto jak możesz go użyć:

wait_selenium_server "/var/log/selenium.log" 5m && \
echo -e "\n-------------------------- Selenium READY --------------------------\n"
Elifarley
źródło