让Bash脚本等待状态消息再继续


10

我正在使用bash脚本启动Selenium服务器,正如您从下面的日志中的时间戳中看到的那样,事情完全在线大约需要32秒:

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

我希望在启动服务器后不使用“ sleep 32”命令(在继续运行之前延迟脚本),我希望bash脚本等待,直到看到字符串“ Started SocketListener”,然后继续。那可能吗?

Answers:


8

您可以用来tail -f随着文件的增长而不断地对其进行读取。小心tail -f输入的内容。您可以通过管道传递tail -f到一个过滤器,该过滤器等待直到所需的日志行并退出。如果tail -f通过管道连接到另一个过滤器的过滤器是行不通的,因为中间过滤器将缓冲其输出。这有效:

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

请注意,我tail是在后台放置的。这是因为,当sed找到所需的行时,它会退出,但是管道会一直在tail等待下一行的运行,这可能根本不会立即出现。tail下一行到来时会退出,并且会收到SIGPIPEtail如果删除日志时未在其中写入任何行,则这可能会留下一个混乱的进程(获得tail进程的PID 可以在sed退出时将其杀死),但很棘手。

¹ 感谢Peter.O指出早期版本中的错误。


一个注释(也许是脚手架)-我不知道实际上需要 noop来允许写入空文件的任何shell 。
克里斯·唐纳

@ChrisDown也没有。但是我发现更明确地编写no-op更加清晰。
吉尔斯(Gilles)'“ SO-别邪恶”

不错(+1)...谈到缓冲区:我不了解缓冲区的伪装,但是如果目标行没有紧随其后的是更多的日志行,那么确实有一个问题需要注意。即使sed已经匹配该模式,它也会挂起直到再写几行为止(<-我不明白那部分)。修改后的sed命令会向日志中写入足以强制使用flush(?)的命令,但确实会立即将其启动(我对其进行了测试),但我认为插入的数据可能会被硒会话线散布。 。
Peter.O

@ Peter.O IIRC一些sed实现会提前阅读下一行,因为在某些情况下它很有用($地址是其中之一)。不过,在我的测试中(使用GNU sed),我没有看到这种情况发生。在什么操作系统上,您是如何确切地展示了该错误?
吉尔(Gilles)'“ SO-别邪恶”

@Gilles:paste.ubuntu.com/863326 ..和:GNU sed版本4.2.1尾部(GNU coreutils)7.4
Peter.O 2012年

3

在直接shell脚本中要难一些,但这是我在tomcat和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

alarm会处理这种情况的tomcat失败的任何潜在挂。从EOF返回的行数是可调的(来自配置文件)。

我最终将整个东西移到了python上。虽然更长,但效率更高:

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

您可以将其添加到脚本中以实现暂停:

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

它利用perl File :: Tail模块的行为类似于tail -f logfile | grep Started SocketListener

用适当的日志文件替换/ var / log / message。请注意,如果从不出现“ Started SocketListener”,它将永远挂起。


1

也许您应该使用超时而不是无限期地等待。

下面的bash函数将阻塞,直到出现给定的搜索词或达到给定的超时为止。

如果在超时时间内找到字符串,则退出状态将为0。

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
}

启动Selenium之后,日志文件可能还不存在。在这种情况下,您应该等待它出现后再搜索字符串:

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

使用方法如下:

wait_selenium_server "/var/log/selenium.log" 5m && \
echo -e "\n-------------------------- Selenium READY --------------------------\n"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.