150

How to create a loop in bash that is waiting for a webserver to respond?

It should print a "." every 10 seconds or so, and wait until the server starts to respond.

Update, this code tests if I get a good response from the server.

if curl --output /dev/null --silent --head --fail "$url"; then
  echo "URL exists: $url"
else
  echo "URL does not exist: $url"
fi
sorin
  • 161,544
  • 178
  • 535
  • 806
  • Can you be more specific about how you are waiting for the server to respond? – chepner Aug 10 '12 at 15:33
  • I will consider that the server is not ready, as long it does not reply or it does reply with something different than a 200 response. – sorin Sep 06 '12 at 13:51

9 Answers9

248

Combining the question with chepner's answer, this worked for me:

until $(curl --output /dev/null --silent --head --fail http://myhost:myport); do
    printf '.'
    sleep 5
done
Serge Stroobandt
  • 28,495
  • 9
  • 107
  • 102
Thomas Ferris Nicolaisen
  • 5,036
  • 2
  • 30
  • 36
  • 15
    The use of backticks ` ` is [outdated](http://unix.stackexchange.com/a/5782/39845). Use `$( )` instead. – Serge Stroobandt Apr 18 '14 at 15:37
  • 10
    Would be a great extension to allow a maximum wait time before giving up – tkruse Mar 17 '16 at 15:20
  • Why do you need a `--head`? – lexicore Feb 03 '17 at 13:10
  • 4
    @lexicore Since the body is not interesting for this command, we only request the head (although you could include logic to check more stuff about the state of the server). Leaving out `--head` wouldn't change anything, but you could if you want to exercise some logic on the response contents (like a status.html). – Thomas Ferris Nicolaisen Feb 05 '17 at 22:30
  • 4
    I had a specific case where using `--head` always returned `405`. Had to remove it to make it work – Ron Harlev Jul 08 '20 at 01:28
  • This didn't work for me, maybe because my server doesn't use the supported protocols, so I used `nc`: https://stackoverflow.com/a/34358304/1121497 – Ferran Maylinch Mar 16 '21 at 08:48
  • 2
    By the way, it looks like `$()` is not recommended, since it would execute the output. At least my IDE is warning me about that. If you remove `$()` it should work anyway, right? – Ferran Maylinch Mar 16 '21 at 08:49
  • The `$()` around the curl command here makes no sense – Michael Kropat Oct 19 '22 at 20:00
64

I wanted to limit the maximum number of attempts. Based on Thomas's accepted answer I made this:

attempt_counter=0
max_attempts=5

until $(curl --output /dev/null --silent --head --fail http://myhost:myport); do
    if [ ${attempt_counter} -eq ${max_attempts} ];then
      echo "Max attempts reached"
      exit 1
    fi

    printf '.'
    attempt_counter=$(($attempt_counter+1))
    sleep 5
done
illagrenan
  • 6,033
  • 2
  • 54
  • 66
  • 2
    I suggest to add `--max-time 5` if for some reasons requested link times out – Enigo Sep 16 '19 at 14:36
  • Very helpful. I attach also the version for deadline-based (rather than attempt-count-based) timeout: `deadline=$(date -d "now + 20 seconds" +%Y%m%d%H%M%S) until ls /test; do echo "waiting ${attempts}"; if [[ "$(date -d now +%Y%m%d%H%M%S)" > "${deadline}" ]]; then echo "waiting timeout reached"; exit 1; fi; attempts=$(($attempts+1)) sleep 3; done` (waits for 20s for presence of `/test` dir and polls every 3s) – Tomáš Záluský Nov 23 '22 at 09:53
18

httping is nice for this. simple, clean, quiet.

while ! httping -qc1 http://myhost:myport ; do sleep 1 ; done

while/until etc is a personal pref.

Bruce Edge
  • 1,975
  • 1
  • 23
  • 31
  • 2
    On second thoughts, forever is a long time...`for i in `seq 60`; do httping -qc1 http://myhost:myport && echo && break sleep 5 echo -n ${i}.. done ` – Bruce Edge Apr 03 '15 at 17:22
14

The poster asks a specific question about printing ., but I think most people coming here are looking for the solution below, as it is a single command that supports finite retries.

curl --head -X GET --retry 5 --retry-connrefused --retry-delay 1 http://myhost:myport
twinlakes
  • 9,438
  • 6
  • 31
  • 42
2

The use of backticks ` ` is outdated.

Use $( ) instead:

until $(curl --output /dev/null --silent --head --fail http://myhost:myport); do
  printf '.'
  sleep 5
done
Serge Stroobandt
  • 28,495
  • 9
  • 107
  • 102
2

You can also combine timeout and tcp commands like this. It will timeout after 60s instead of waiting indefinitely

timeout 60 bash -c 'until echo > /dev/tcp/myhost/myport; do sleep 5; done'

Manikandan
  • 3,025
  • 2
  • 19
  • 28
2

The following snippet:

  • Wait's until all URLs from the arguments return 200
  • Expires after 30 second if one URL is not available
  • One curl requests timeouts after 3 seconds

Just put it into a file and use it like a generic script to wait until the required services are available.

#/bin/bash

##############################################################################################
# Wait for URLs until return HTTP 200
#
# - Just pass as many urls as required to the script - the script will wait for each, one by one
#
# Example: ./wait_for_urls.sh "${MY_VARIABLE}" "http://192.168.56.101:8080"
##############################################################################################

wait-for-url() {
    echo "Testing $1"
    timeout --foreground -s TERM 30s bash -c \
        'while [[ "$(curl -s -o /dev/null -m 3 -L -w ''%{http_code}'' ${0})" != "200" ]];\
        do echo "Waiting for ${0}" && sleep 2;\
        done' ${1}
    echo "${1} - OK!"
}

echo "Wait for URLs: $@"

for var in "$@"; do
    wait-for-url "$var"
done

Gist: https://gist.github.com/eisenreich/195ab1f05715ec86e300f75d007d711c

Daniel Eisenreich
  • 1,353
  • 3
  • 16
  • 32
1
printf "Waiting for $HOST:$PORT"
until nc -z $HOST $PORT 2>/dev/null; do
    printf '.'
    sleep 10
done
echo "up!"

I took the idea from here: https://stackoverflow.com/a/34358304/1121497

Ferran Maylinch
  • 10,919
  • 16
  • 85
  • 100
0

Interesting puzzle. If you have no access or async api with your client, you can try grepping your tcp sockets like this:

until grep '***IPV4 ADDRESS OF SERVER IN REVERSE HEX***' /proc/net/tcp
do
  printf '.'
  sleep 1
done

But that's a busy wait with 1 sec intervals. You probably want more resolution than that. Also this is global. If another connection is made to that server, your results are invalid.

BoeroBoy
  • 1,094
  • 11
  • 17