Writing ping() in Julia

TL;DR:

A simple cross-platform ping() function which returns true/false in Julia 1.x can be written

function ping(host::String = "8.8.8.8")
    try
        run(pipeline(`ping -$(Sys.iswindows() ? "n" : "c") 1 $host`, devnull))
        return true
    catch
        return false
    end
end

Background

In general, when testing for Internet connection, my go-to is to check that pinging one of Google’s DNS addresses (8.8.8.8) returns an appropriate response. In Python this can be achieved with what is essentially a one-liner:

import platform, subprocess
def ping(host):
    try:
        subprocess.check_output("ping -{} 1 {}".format('n' if  platform.system().lower() == "windows" else 'c', host), shell = True)
        return True
    except:
        return False

Owing to its relative infancy, Julia 1.x lacks the many publicly available packages and plethora of questions asked over many years that Python enjoys. Consequently, I struggled to find a similarly simple function in Julia – inbuilt or otherwise – so I decided to write my own.

Composition

Prior to composing my own ping() function, I searched for an existing solution to the problem, and only came across a post from the Julia discourse forum in April 2017 which I thought would prove helpful. The (unofficially) accepted answer was given by

julia> match(r"time=(.*)$", split(readstring(`ping -c 1 8.8.8.8`) , "\n", keep=false)[2])[1]

Whilst the question regarded the timing of pings, I figured that I could scavenge the core components and use them for my own purpose. The problem with this solution was that the readstring() function had since been deprecated and no longer existed as part of the language. Consequently, my ping() would have to be written from scratch.

v1.0

The first iteration of my code was rather naïve and brutish, but functional. Firstly, I looked for a way of executing shell commands from within Julia and came across the Cmd objects and the run() function. Utilising these, the starting point was hence

function ping()
    run(`ping -c 1 8.8.8.8`)
end

OS Generality

The most obvious issue with this was that the -c option is Unix-specific, whereas the intent was to produce a cross-platform function; as such, I found Julia’s inbuilt methods of finding the operating system. Furthermore, one might wish to ping a different host whilst having a default upon which to fall back. A step toward abstraction then resulted in the code becoming

function ping(host::String = "8.8.8.8")
     opt::String = ""
 
     if Sys.iswindows()
         opt = "-n"
     else
         opt = "-c"
     end
 
     run(`ping $opt 1 $host`)
 end

Streamlining

The next improvement was to attempt to shorten this code, akin to the earlier Python script. Luckily, Julia has a terse conditional evaluation notation in the form of the ternary operator ?: which can be employed to remove the if-else above:

function ping(host::String = "8.8.8.8")
    run(`ping -$(Sys.iswindows() ? "n" : "c") 1 $host`)
end

Booleanisation

By this point the ping() function was a one-liner which was able to ping a host of choice. However, it was yet unable to distinguish whether the response was a positive one. Fortuitously, Julia throws an errors when a run() command fails, and so by encapsulating the function within a try-catch, it became possible to return boolean values;

function ping(host::String = "8.8.8.8")
    try
        run(`ping -$(Sys.iswindows() ? "n" : "c") 1 $host`)
        return true
    catch
        return false
    end
end

Suppression

One might note that running this results in the ping output being printed in stdout. However, I wished only to know whether it was successful without the details of the message transaction – I therefore wanted to suppress the output. The pipeline method allows output forwarding, which in this case was to be the operating system’s null location. On Windows systems this is nul whereas Unix systems it is /dev/null. One could use another ternary operator to choose the appropriate null, however Julia has an inbuilt devnull constant which generalises the null locations. A combination of these hence permits pings to be sent without cluttering the terminal:

function ping(host::String = "8.8.8.8")
    try
        run(pipeline(`ping -$(Sys.iswindows() ? "n" : "c") 1 $host`, devnull))
        return true
    catch
        return false
    end
end

Leave a Reply

Your email address will not be published.