Uma simplificação adicional da resposta de Martynas:
until ping -c1 www.google.com &>/dev/null; do :; done
observe que o próprio ping é usado como o teste de loop; assim que for bem sucedido, o loop termina. O corpo do loop está vazio, com o comando nulo " :
" usado para evitar um erro de sintaxe.
Update: Eu pensei em uma maneira de fazer o Control-C sair do loop de ping de forma limpa. Isso executará o loop em segundo plano, interceptará o sinal de interrupção (Control-C) e eliminará o loop de segundo plano se ocorrer:
ping_cancelled=false # Keep track of whether the loop was cancelled, or succeeded
until ping -c1 "$1" &>/dev/null; do :; done & # The "&" backgrounds it
trap "kill $!; ping_cancelled=true" SIGINT
wait $! # Wait for the loop to exit, one way or another
trap - SIGINT # Remove the trap, now we're done with it
echo "Done pinging, cancelled=$ping_cancelled"
É um pouco tortuoso, mas se você quiser que o loop seja cancelável, ele deve fazer o truque.