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
- Wait upwards of ten seconds for an IP on
eth0
_ip_addresses_list=($(await_ipv4_address 'eth0'))
- Wait upwards of thirty seconds for an IP on
tun0
_ip_addresses_list=($(await_ipv4_address 'tun0' '1' '29'))
- Wait upwards of a minuet for an IP on
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 of Special Parameters
section as well as the man --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
- should be used with care because if state is not checked and updated correctly the loop will never exit.
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
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}"
[[ "${#_ipv4_addresses[@]}" -gt '0' ]]; return "${?}"
- Bash semicolons with testing brackets (
[[ 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 test
If there's something questionable after all that feel free to post a comment so that the answer can be improved.