I have a couple ideas on how I would achieve this. Not sure how I would script it.
Method 1: (probably the better choice)
Create a loop that pings a server u
This command should wait until it can contact google or it has tried 50 times:
for i in {1..50}; do ping -c1 www.google.com &> /dev/null && break; done
The for i in {1..50}
loops 50 times or until a break
is executed. The ping -c1 www.google.com
sends 1 ping packet to google, and &> /dev/null
redirects all the output to null, so nothing is outputed. && break
executes break only if the previous command finished successfully, so the loop will end when ping
is successful.
How would one go about adding this functionality in a script -- Geofferey
Following be part of how I'm going about solving a similar situation in a modular fashion...
await-ipv4-address.sh
#!/usr/bin/env bash
## Lists IP addresses for given interface name
## @returns {number|list}
## @param {string} _interface - Name of interface to monitor for IP address(es)
## @param {number} _sleep_intervel - Number of seconds to sleep between checks
## @param {number} _loop_limit - Max number of loops before function returns error code
## @author S0AndS0
## @copyright AGPL-3.0
## @exampe As an array
## _addresses_list=($(await_ipv4_address 'eth0'))
## printf 'Listening address: %s\n' "${_addresses_list[@]}"
## #> Listening address: 192.168.0.2
## #> Listening address: 192.168.0.4
## @example As a string
## _addresses_string="$(await_ipv4_address 'eth0' '1' '3')"
## printf 'Listening address(es): %s\n' "${_addresses_string}"
## #> Listening address(es): 192.168.0.2 192.168.0.4
await_ipv4_address(){
local _interface="${1:?# Parameter_Error: ${FUNCNAME[0]} not provided an interface}"
local _sleep_interval="${2:-1}"
local _loop_limit="${3:-10}"
if [ "${_sleep_interval}" -lt '0' ] || [ "${_loop_limit}" -le '0' ]; then
printf 'Parameter_Error: %s requires positive numbers for second and third parameters\n' "${FUNCNAME[0]}" >&2
return 1
fi
local _loop_count='0'
local -a _ipv4_addresses
while true; do
for _address in $({ ip addr show ${_interface} | awk '/inet /{print $2}'; } 2>/dev/null); do
_ipv4_addresses+=("${_address}")
done
if [ "${#_ipv4_addresses[@]}" -gt '0' ]; then
printf '%s\n' "${_ipv4_addresses[*]}"
break
elif [ "${_loop_count}" -gt "${_loop_limit}" ]; then
break
fi
let _loop_count+=1
sleep "${_sleep_interval}"
done
[[ "${#_ipv4_addresses[@]}" -gt '0' ]]; return "${?}"
}
Source for above are on GitHub bash-utilities/await-ipv4-address, check the ReadMe file for instructions on utilizing Git for updates and bug fixes.
To source the above function within current shell...
source "await-ipv4-address.sh"
... or within another script...
#!/usr/bin/env bash
## Enable sourcing via absolute path
__SOURCE__="${BASH_SOURCE[0]}"
while [[ -h "${__SOURCE__}" ]]; do
__SOURCE__="$(find "${__SOURCE__}" -type l -ls | sed -n 's@^.* -> \(.*\)@\1@p')"
done
__DIR__="$(cd -P "$(dirname "${__SOURCE__}")" && pwd)"
source "${__DIR__}/modules/await-ipv4-address/await-ipv4-address.sh"
Would awk or grep be of use in a situation like this? -- Geofferey
Both may be used; though much like echo
ing I think grep
ing is best done in the privacy of one's own shell... I prefer awk
in public as there's a whole scripting language to facilitate feature creep, and printf
because it's not as likely to have missing features on slimmed-down environments.
Here's how to awk
an address regardless of IPv4 vs. IPv6 flavor....
# ... trimmed for brevity
for _address in $({ ip addr show ${_interface} | awk '/inet/{print $2}'; } 2>/dev/null); do
# ... things that get done with an address
done
... just a difference in space to get more data.
Something I can have be compatible regardless of interface name or type
Three different interface name examples and how to overwrite some of the default behavior
eth0
_ip_addresses_list=($(await_ipv4_address 'eth0'))
tun0
_ip_addresses_list=($(await_ipv4_address 'tun0' '1' '29'))
wlan0
while sleeping 3 seconds between checks_ip_addresses_list=($(await_ipv4_address 'wlan0' '3' '19'))
Note, if
await_ipv4_address
gets board it'll return a non-zero status, so the following...
_ip_addresses_list=($(await_ipv4_address 'wlan0' '3' '19' || true))
... may be used if you've got error traps that get tripped by such things.
Then do stuff with the IP addresses once assigned...
for _ip_address in "${_ip_addresses_list[@]}"; do
printf 'IP -> %s\n' "${_ip_address}"
done
Wait for network interface to do what? -- user207421
to be up, but more than up lol I need an active connection where I'm sure I can connect to internet -- Geofferey
The above will not test for an active connection to the greater Internet, only if an IP address has been assigned via the local network switch/AP or static settings; though, a local IP is a prerequisite... consider the above part of an answer that is script friendly as it's only designed to preform one thing well.
To reliably detect if connections to the rest of the world wide web are permitted check out dig
and curl
, because a successful ping
to one's favorite DNS does not mean other protocols are allowed.
Could you explain each part of your script so I am not blindly using something without having an understanding of how it works? -- Geofferey
... Sure...
await_ipv4_address(){
local _interface="${1:?# Parameter_Error: ${FUNCNAME[0]} not provided an interface}"
local _sleep_interval="${2:-1}"
local _loop_limit="${3:-10}"
# ...
}
local
assigns locally scoped variables, help local
and help declare
will show some useful documentation on more advanced usage
"${something:?Error message}"
will print Error message
if something
is not assigned
"${another_thing:-1}"
will default to 1
if another_thing
is not assigned or assigned an null value
Hint,
man --pager='less -p ^"PARAMETERS"' bash
through till end ofSpecial Parameters
section as well as theman --pager='less -p "Parameter Expansion"' bash
section may be helpful in finding more things that can be done with variables and stuff.
if [ "${_sleep_interval}" -lt '0' ] || [ "${_loop_limit}" -le '0' ]; then
printf 'Parameter_Error: %s requires positive numbers for second and third parameters\n' "${FUNCNAME[0]}" >&2
return 1
fi
throws errors if either _sleep_interval
or _loop_count
are not numbers because of less-than (-lt
) and less-than or equal-to (-le
) checks
throws error if either of if
checks return true
, the ||
chains multiple checks such that if the left side returns false
it trips the right side for a check, where as &&
would only fire if the left side returned true
hint
man operator
will show directionality of various operators
printf 'something\n' >&2
writes something
to standard error; where all well-behaved errors should be written so that logs can be made or output ignored
shows a level of paranoia about function inputs that may be excessive
while true; do
# ... stuff
done
for _address in $({ ip addr show ${_interface} | awk '/inet /{print $2}'; } 2>/dev/null); do
_ipv4_addresses+=("${_address}")
done
the $({ command | parser; } 2>/dev/null)
trick is something that I picked-up from around these parts
$(something)
runs something
within a sub-shell{ one_thing | another_thing; }
is a compound commandHint,
man --pager='less -p "Compound Commands"' bash
should show relevant documentation
2>/dev/null
causes standard error to be written where no input returns_preexisting_list+=("element")
appends element
to _preexisting_list
by way of +=
if [ "${#_ipv4_addresses[@]}" -gt '0' ]; then
printf '%s\n' "${_ipv4_addresses[*]}"
break
elif [ "${_loop_count}" -gt "${_loop_limit}" ]; then
break
fi
if
part checks if the number of elements within _ipv4_addresses
are greater than 0
via the #
sign, ${#_list_name[@]}
elif
part checks if function should be board by now
In either case a break
from the while
loop is taken when logic is tripped.
let _loop_count+=1
sleep "${_sleep_interval}"
let _counter+=1
will add 1
to whatever previous value was in _counter
and assign it to _counter
sleep
causes loop to chill out for a number of seconds so other things can be contemplated by the device
[[ "${#_ipv4_addresses[@]}" -gt '0' ]]; return "${?}"
[[ is_it_true ]]
) instead of ||
or &&
causes return
to return the status of if the number of IP addresses found where greater than 0
regardless of truthiness of testIf there's something questionable after all that feel free to post a comment so that the answer can be improved.
I've tested this on a board which is configured via DHCP.
The assumption is that if a default gateway exists on a specific interface (in this case eth0
), then this is due to the fact that the board has gotten assigned an IP (and thus the default gateway) by the DHCP server, which implies that networking is up and running.
My issue was that for me networking is considered to be up as soon as machines in the LAN/intranet can be accessed, even if there exists no internet connectivity (ie 8.8.8.8 or www.google.com is not accessible). I also don't want to ping specific IPs or domain names in the intranet because I don't want to make assumptions about the subnet or which devices will definitely be up and what their IP or domain name is.
while ! ip route | grep -oP 'default via .+ dev eth0'; do
echo "interface not up, will try again in 1 second";
sleep 1;
done