I'm firing up Selenium server with a bash script and as you can see from the timestamps on the log below, it takes about 32 seconds for the thing to fully come 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:
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
22:19:34.555 INFO - Started org.openqa.jetty.jetty.Server@7d29f3b5

Instead of using a "sleep 32" command after starting the server (to delay the script before moving on), I'd like my bash script to wait until it sees the string "Started SocketListener", and then continue. Is that possible?

4 Answers 4


You can use tail -f to keep reading from the file as it grows. Be careful with what you feed tail -f into. You can pipe tail -f into a filter that waits until the desired log line and quits. What won't work is if you pipe tail -f into a filter that pipes into another filter, because the intermediate filter will buffer its output. This works:

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

Note that I put tail in the background. This is because when sed finds the desired line, it exits, but the pipeline keeps running as long as tail is waiting for the next line, which may not come immediately if at all¹. tail will exit when the next line comes and it receives a SIGPIPE. This may leave a stray tail process if the log is removed without any line being written to it (obtaining the PID of the tail process to kill it when sed exits would be possible but tricky).

¹ Thanks to Peter.O for pointing out the bug in an earlier version.

  • A note (pedantry, perhaps) -- I don't know any shells that actually require a noop to allow writing an empty file.
    – Chris Down
    Mar 1, 2012 at 1:24
  • @ChrisDown Neither do I. But I find it clearer to write the no-op explicitly. Mar 1, 2012 at 1:27
  • Nice (+1) ...speaking of buffers: I don't understand the buffering machinations, but it does have one issue to be aware of, if the target line is not immediately followed by more log lines. It hangs untill more lines are written, even though sed has already matched the pattern (<-- I don't understand that part). A modified sed command which writes enough to the log to force a flush(?), does kick it on immediately (I tested it), but I think that has the possibility of the inserted data to be interspersed with a selenium-session line...
    – Peter.O
    Mar 1, 2012 at 11:50
  • @Peter.O IIRC some sed implementations read the next line in advance, because there are a cases where it's useful (the $ address, for one). I didn't see that happen in my tests though (with GNU sed). How exactly did you exhibit the bug, on what OS? Mar 1, 2012 at 12:07
  • @Gilles: paste.ubuntu.com/863326 .. and: GNU sed version 4.2.1, tail (GNU coreutils) 7.4
    – Peter.O
    Mar 1, 2012 at 12:27

It is a little harder in straight shell script, but this is what I was using for quite a while for tomcat and oc4j:

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

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

The alarm will handle any potential hanging where tomcat failed. The number of lines to go back from the EOF is adjustable (from a config file).

I eventually moved the entire thing to python; while it is a bit longer, it is a bit more efficient:

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
    def __del__(self):
    def __nonzero__(self):
       return self.state
    def clear(self):
       self.state = False
    def _start_alarm(self):
       from signal import alarm
    def _set_sigalarm(self, handler):
        if handler:
            self.signal_signal(self.signal_SIGALRM, handler)
            self.signal_signal(self.signal_SIGALRM, self.signal_SIG_DFL)
    def reset_signals(self):
    def set_signals(self):
    def handler(self, signo, frame):
        self.state = False
    def start(self):
        self.state = True
    def stop(self):
        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)
    while alarm:
        line = logfile.readline()
        if line:
            m = scanfor.search(line)
            if m:
                found = True

You can add this to your script to implement the pause:

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

It makes use of the perl File::Tail module to behave like tail -f logfile | grep Started SocketListener.

Replace /var/log/message with the appropriate log file. Note that it will hang forever if "Started SocketListener" never appears.


Maybe you should use a timeout instead of waiting indefinitely.

The bash function below will block until the given search term appears or a given timeout is reached.

The exit status will be 0 if the string is found within the 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

Perhaps the log file doesn't exist yet just after launching Selenium. In that case, you should wait for it to appear before searching for the string:

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


Here's how you can use it:

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .