Using utils.shellexecute for ping results?

Posted by AdInfinitum on Thu 02 Aug 2018 04:55 PM — 9 posts, 33,612 views.

#0
Currently, I have an alias as follows:

<aliases>
  <alias
   match="^ping (.*?) (.*?)$"
   enabled="y"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>n = "C:/MUSHClient/pingresults.txt"
pingmin, pingmax, pingavg = nil, nil, nil

os.execute("ping %2 > " .. n .. " &")

for line in io.lines(n) do
    if string.find(line, "Minimum =") then
        pingmin = string.match(line, "Minimum = (%d+ms)")
    end
    if string.find(line, "Maximum = ") then
        pingmax = string.match(line, "Maximum = (%d+ms)")
    end
    if string.find(line, "Average =") then
        pingavg = string.match(line, "Average = (%d+ms)")
    end
end

if pingmin and pingmax and pingavg then
    Send("%1 @r{@x111Ping to @W%2 @x111Min: @w" .. pingmin .. " @x111Avg: @w" .. pingavg .. " @x111Max: @w" .. pingmax .. "@r}$C")
else
    Send("%1 @r{@x111Unsuccessful ping to @w%2@r}$C")
end

os.remove(n)</send>
  </alias>
</aliases>


This works properly, except it opens up a command window, which freezes game play for a few seconds while it executes the command.

Last night, I came across utils.shellexecute, and thought this might be a great substitute for os.execute, so I decided to play with it. However, I cannot seem to get it to work. If I try:


utils.shellexecute("ping", "aardmud.org")


... then it works. But I need to store the results in a text file, and when I try:


utils.shellexecute("ping", "%2 > " .. n .. " &", GetInfo(68), "open", 0)


... in place of the os.execute, it doesn't seem to generate the text file, evidenced the by error I get that there is nothing to be read.

I've also tried, to no avail:

utils.shellexecute("cmd", "ping %2 > " .. n .. " &", GetInfo(68), "open", 0)


Is there a way I can accomplish what I want? Or is this just a fruitless venture?
Australia Forum Administrator #1
See: https://www.gammon.com.au/scripts/doc.php?lua=utils.shellexecute

You haven't exactly done what is suggested there (near the bottom). In particular use the /C parameter.
#2
Ah, for some reason, it hadn't clicked that the /C was required. Now the ping properly works, but instead of waiting for the results to be put into the pingresults.txt file, it attempts to read it right away.

What way would you suggest is the best way to handle that situation? I could easily use wait, but that would require me to put some arbitrary number of seconds that may or may not be long enough. If possible, I'd rather have it wait until the ping command was finished.

Maybe if I do a loop checking if the file exists?
Australia Forum Administrator #3
I've confronted this issue in the past.

Quote:

This works properly, except it opens up a command window, which freezes game play for a few seconds while it executes the command.


...

Quote:

... but instead of waiting for the results to be put into the pingresults.txt file, it attempts to read it right away.


Well, do you want to wait or not?

You could delete the file, run the command, and then loop checking if the file has been created (which will, of course, introduce a delay).

It's not that you want the file created, too, it's that you want it to be finished written to. If you wait for the file to appear, it might have 0 bytes in it.

These aren't easy problems to solve. You might delete the file, write to a different file, then rename it when done. This isn't so much a MUSHclient issue, it's the way that reality works. Things don't happen instantly. Either you wait for them to finish, or you work around that.

Possibly start the file writing, and then, using a timer, check every half second or so for the (renamed) file to appear. This could be done with the "wait" module.
#4
Regarding waiting, I did need it to wait to finish writing to the log. I wound up using wait.make in order to accomplish it, setting it at 5 seconds, figuring if it wasn't finished pinging in 5 seconds, then I had a really bad connection anyway. Here's the finished product now:

require "wait"

n = "C:/MUSHClient/pingresults.txt"
pingmin, pingmax, pingavg = nil, nil, nil

wait.make(function ()

    assert(utils.shellexecute("cmd", "/C ping %2 > " .. n .. " &", GetInfo(68), "open", 0))

    wait.time(5)
    
    for line in io.lines(n) do
        if string.find(line, "Minimum =") then
            pingmin = string.match(line, "Minimum = (%d+ms)")
        end
        if string.find(line, "Maximum = ") then
            pingmax = string.match(line, "Maximum = (%d+ms)")
        end
        if string.find(line, "Average =") then
            pingavg = string.match(line, "Average = (%d+ms)")
        end
    end

    if pingmin and pingmax and pingavg then
    &#9;Send("%1 @r{@x111Ping to @W%2 @x111Min: @w" .. pingmin .. " @x111Avg: @w" .. pingavg .. " @x111Max: @w" .. pingmax .. "@r}$C")
    else
        Send("%1 @r{@x111Unsuccessful ping to @w%2@r}$C")
    end

    os.remove(n)
end)


The %1 and %2 are channel and site you're pinging, respectively, if anyone is interested in using it.

Thanks for guiding me to the right path - this provides uninterrupted play while I ping something, and not having to worry about a window opening up has been a blessing! I wish I had known about this sooner!
Australia Forum Administrator #5
You should probably do an io.open to check the file exists, otherwise it would crash if it doesn't.

io.lines raises an error if it can't open the file. Instead you can use io.open and check for a nil return.
USA Global Moderator #6
Quote:
I wound up using wait.make in order to accomplish it, setting it at 5 seconds


But you don't want to always wait 5 seconds. Make it wait only 0.1 seconds in a loop until either the file is written or 5 seconds has elapsed from when you started. That way you'll get your response as soon as possible.
Australia Forum Administrator #7
You don't want a half-written file, so waiting a bit of extra time would help, whatever "extra" means in this context. Possibly waiting until the file size has ceased to change, however the file size may only change when the output is flushed to disk, so that might not mean anything.

Possibly you could detect if the size is greater than some minimum, then it has probably been written to. (Because of the flushing aspect, the minimum might well be zero).
#8
I think what I can do in this case is loop until the file size is greater than 1 kb. The file is made at the start, but it's empty. It doesn't get written to until the ping has completed. As such, as long as the file size is greater than 1 kb, I should be okay. For all intents and purposes, I could even increase that to about 80 kb to make sure there's something of substance actually written.

Thanks for the great feedback!