waiting for network in a bash script

33,630

Solution 1

Shell syntax

You seem to be confused regarding conditionals in shell scripts. Every shell command has an exit status, which is an integer between 0 and 255, with 0 meaning success and any other value meaning failure. Statements like if and while that expect boolean operands inspect the exit status of the command and treat 0 (success) as true and any other value (failure) as false.

For example, the grep command returns 0 if the pattern is found and 1 if the pattern is not found. So

while ifconfig | grep "192.168.100." > /dev/null; do …

repeats the loop as long as the pattern 192.168.100. is found in the output of ifconfig. Note that the pattern 192.168.100. matches strings like 192x168 1007, because . in a regular expression matches any character; to search for a literal string, pass the option -F to grep. To invert the condition, put ! in front.

while ! ifconfig | grep -F "192.168.100." > /dev/null; do …

Further in the script, you want to compare the value of a variable to a number. You use the -gt operator, which is part of the syntax of the of conditional expressions understood by the test command. The test command returns 0 if the conditional expression is true and 1 if the conditional expression is false.

if test "$x" -gt 200; then

It is customary to use the alternate name [ for the test command. This name expects the command to end with the parameter ]. The two ways of writing this command are exactly equivalent.

if [ "$x" -gt 200 ]; then

Bash also offers a third way to write this command, with the special syntax [[ … ]]. This special syntax can support a few more operators than [, because [ is an ordinary command subject to the usual parsing rules, while [[ … ]] is part of the shell syntax.

Again, keep in mind that [ is for conditional expressions, which are a syntax with operators like -n, -gt, … [ doesn't mean “boolean value”: any command has a boolean value (exit status = 0?).

Detecting that the network is up

Your way of detecting that the network is up is not robust. In particular, note that your script will be triggered as soon as any network interface acquires an IP address within the specified range. In particular, it's quite possible that DNS won't be up yet at that point, let alone any network shares mounted.

Do you really need to run these commands when someone logs in? It's easier to make a command run automatically when the network is brought up. The way to do that depends on your distribution and whether you use NetworkManager.

If you need to run these commands as part of the login scripts, then test for the resource that you really need, not for the presence of an IP address. For example, if you want to test whether /net/somenode/somedir is mounted, use

while ! grep -q /net/somenode/somedir </proc/mounts; do
  sleep 1
done

If you have upstart or systemd…

then you can use it. For example, with Upstart, mark your job as start on net-device-up eth0 (replace eth0 by the name of the interface that provides the desired network connectivity). With Systemd, see Cause a script to execute after networking has started?

Solution 2

Just count the output of ip add show For example:

root@xxxxxxlp01 ~ $ ip add sh dev eth3 | grep inet
root@xxxxxxlp01 ~ $ ip add sh dev eth1 | grep inet
root@xxxxxxlp01 ~ $ ip add sh dev eth0 | grep inet
    inet xxx.xxx.64.91/24 brd xxx.xxx.95.255 scope global eth0
    inet6 fe80::224:e8ff:fe78:4dfb/64 scope link
root@xxxxxxlp01 ~ $ ip add sh dev eth0 | grep inet | wc -l
2

Then you can just branch on the number of line returned as appropriate. You'd probably just check whether it was zero, and if so, sleep and repeat the loop.

Untested code for illustration:

while [ 1 ]; do

  if [ $(ip add sh dev eth0 | grep inet | wc -l) -ne 0 ]; then
     break
  fi

  sleep 1

done

Solution 3

You can use the hostname command.

with option -I, you get your local IP address, which would be the one that your DHCP server returns.

hostname -I
# (if has network) return: 192.168.100 or other IP local

Check this very simple example:

while [ "$(hostname -I)" = "" ]; do
  echo -e "\e[1A\e[KNo network: $(date)"
  sleep 1
done

echo "I have network";

I've been thinking and checking the other answers.

And maybe if you want to check the connectivity with your server.

I transcribe the same script, but now waiting for connectivity with the server.

# ip server
serverAdr="192.168.0.1"

ping -c 1 $serverAdr > /dev/null 2>&1
while [ $? -ne 0 ]; do
  echo -e "\e[1A\e[K $(date): Connecting - ${serverAdr}"
  sleep 1
  ping -c 1 $serverAdr > /dev/null 2>&1
done

echo "$(date): Connected - ${serverAdr}";

Solution 4

Ping will return a 0 response if at least one attempt was successful. You might consider pinging the server you are connecting to until it is successful.

You can even prevent your script to wait forever using ping -c.

For example, this code try 10 times to contact 192.168.0.1, waiting 10s between each attempt, and exit if no attempt was successful :

ping -c 10 -i 10 192.168.0.1 || exit 1
Share:
33,630

Related videos on Youtube

aguywithsocks
Author by

aguywithsocks

Updated on September 18, 2022

Comments

  • aguywithsocks
    aguywithsocks almost 2 years

    I'm running a script that relies on network being up and a network share be mounted. The script runs on login (which happens automatically after boot). The problem is that by the time the script runs, I usually do not have an IP address yet (DHCP). At the moment I just sleep the script for 15s, but I don't like this approach at all, since I want to be able to tell the user if something is wrong.

    What my plan is, is loop while I don't have an IP address yet and continue when I do. Crucially, it has to time out after a while. What I came up with is to if [ ifconfig | grep "192.168.100" ]; but what happens is that grepconsumes the ]; and doesn't like it. Then bash also gets upset, because it can't find the ]; which grep ate. And then I haven't even implemented the time-out.

    Someone suggested keeping a variable, and sleeping for, say, a second in each iteration and increase this variable each time. Here is my complete (non working) script (I'm fairly new to bash scripting):

    x=0
    while [ ifconfig | grep "192.168.100." > /dev/null ]; do
        echo "no nework"
        if "$x" -gt 200; then
            #Time out here
            exit 1
        x=$((x+1))
        sleep .1
        fi
    done
    
    #continue with rest of script...
    

    Any pointers in the right direction would be greatly appreciated!

    • jasonwryan
      jasonwryan almost 10 years
      If your init is systemd, write a service file that waits for network-online.target...
    • rush
      rush almost 10 years
      no need to redirect grep output to /dev/null. Use -q option instead. And also I don't thing grepping ifconfig is a good idea. Why not to use ping instead?
    • Bratchley
      Bratchley almost 10 years
      ping can fail depending on the network in question. It's better to just check the system's running configuration.
  • sebix
    sebix over 9 years
    I guess /net/ should be /proc/net?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 9 years
    @sebix No, why would it? I'm not talking about Linux's network configuration. I'm giving an example of a mount point for a remote filesystem, /net is a common location for such mount points.
  • sebix
    sebix over 9 years
    Ok, so you require a mounted network drive. Does a more generic solution also exist?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 9 years
    @sebix A solution for what? Requiring a mounted network drive was an example of a goal, not an example of a solution.