How to loop a script yet stop it easily on external command?

Enlightened ones: Can you suggest how I could run a small script repeatedly, yet stop it easily if I need to?

Background is that I use Citrix Workspace to run apps on a Windows server, and it times out after just a few minutes of inactivity. Reconnecting involves a password and an SMS message, so it’s a pain.

So I wrote an AppleScript that runs a loop: every five minutes it checks to see whether Citrix Workspace is running in the background. If that’s so, it activates Citrix Workspace, types a harmless keystroke, and returns to the app that was previously in the front. It’s not elegant but it seems to work.

I’ve set the script to run for a couple of hours once it’s started. Once it’s running, though, I would like a way to break out without force-quitting Script Editor (or the app that I could build from the script). Some feedback about the length of time left on the clock would be good, too.

I could put the whole script in a repeat loop that puts up a dialog box and checks for input (if button returned of the result is "Stop" then exit repeat).

But is there a better way, perhaps by taking the loop out of the AppleScript and using some other technique (cron?) to run it every five minutes? Ideally I’d control it from the menubar but that’s probably a step too far.

I reckon I’m missing a trick somewhere and I’d be grateful for creative suggestions.

I’m not sure this qualifies as “creative”, but you could periodically check for the presence of the file /tmp/stop-script, and if the file exists, delete the file and quit the script.

To trigger the quit, you could “touch /tmp/stop-script” from Terminal, or you could run a small script to create the file.

You probably should check for the file’s presence more often than every five minutes, perhaps every second or so, so the script quits quickly.

Your main script could start a separate “stopping” script which puts up a tiny window with a “Quit” button on your screen, and leaves it on the screen all the time. When you click the “Quit” button, the app creates the file and exits.

Or a “stopping” script could be built into a “menu bar” app, but that’s more complicated. (Note that the area on the right side of the menu bar is actually called the “status bar” in macOS documentation.) If I recall correctly, you need to set an app’s “NSUIElement” Info.plist entry to the string “1”, and within the script you need to put an icon for the app in the status bar and handle clicks on the icon.

This sounds like something that should be a cron job running every five minutes rather than have an infinite loop.

Lingon iirc is a great tool for adding cron type jobs.

https://www.peterborgapps.com/lingon/

1 Like

Thank you both very much. @JKBull: A clever idea about the temporary file (I use a similar technique with a single-user FileMaker database to make sure it’s open in only one place at a time). I do like the sound of the menu bar. Maybe it’s time I learned to do small things in Xcode…

@ddmiller: Yes, cron feels as if it might be more robust. Thank you for the reminder about Lingon, which I used quite a long time ago. For everyday use it would be good to control cron from a shell script, which I see can be done, though it would a big challenge for me.

1 Like

I think that cron is probably a good solution, but if you’re leaning towards making an app, look into Platypus, a simple and clever utility for easily wrapping a script with a GUI.

Ooh, thank you! Platypus looks interesting and I like the menu bar and progress bar stuff. Not updated since 2018 but I assume it still works fine for you.

I may be missing something here, but if you’re already checking whether or not Citrix is running, why not just save that state as a Boolean, and if it’s not running, either don’t take the proscribed actions, or exit the script, or quit the applet you make from said script.

Something like:


tell application “System Events”
repeat every 300 seconds 
    set isCitrixRunning to (exists process “Citrix Desktop”)
    —> TRUE/FALSE

if isCitrixRunning is true then
   (* do some stuff I shouldn’t have to do manually *)
else
   return 
(* or keep checking via loop until you quit your applet, etc.; or ‘exit repeat’ or ‘quit me’ *)
end if
end repeat
end tell

But, yes, using LaunchAgents is another way to fire your ‘keep alive’ AppleScript.

I would be more interested in finding a way to do this without bringing Citrix to the foreground; if I’m banging out text somewhere else, the last thing I want is for Citrix to steal my keystrokes every five minutes, no matter how briefly. This might be possible by using GUI scripting to trigger a Menubar item or shortcut in Citrix without bringing it to foreground; Keyboard Maestro can also accomplish this. I suppose it’s not likely for Citrix itself to be Scriptable?

Edit to add: yes, you can also control this from the Menubar using Swiftbar for a custom Menubar item, or using FastScripts to create a kill switch; but if you build an Applet, it’s always going to be in the Dock, anyway. LaunchAgents will definitely be cleaner, all around.

HTH

Frederico

Thank you @frederico! The only reason for repeating the check on whether Citrix is running is because I want the script to run for an entire work session – a couple of hours, say – and so conceivably I might quit the app during that time, which would upset the script. But a try command would probably work just as well.

if I’m banging out text somewhere else, the last thing I want is for Citrix to steal my keystrokes every five minutes

Quite. The script gives a warning beep before transferring control, but it’s still a pain. However, I can’t see a way to control Citrix in the background, and in any case that’s a separate question that’s probably not of interest to most folks here.

For the record, Citrix Workspace has a scripting dictionary with the Standard Suite, which basically limits me to moving the window around, and I don’t think that will help (though I’ll try). My actual working window is something called Citrix Viewer, which isn’t scriptable (and similarly for various other Citrix processes). Frustratingly, I believe Citrix Workspace is a successor to Citrix Receiver, which seems to be properly scriptable (it appears in the Citrix Workspace dictionary) but isn’t among my running processes.

The Citrix setup actually works quite well and I prefer it to messing around with a company-issued Windows machine. But it’s frustratingly picky about wanting me to re-authenticate, even sometimes when I’m working entirely within the app. Citrix supports smartcards, but the IT department doesn’t.

I think you may have misunderstood me, you should place the check for whether Citrix is running or not within the repeat loop; in this way you can gracefully exit the script or applet if Citrix is no longer running. Also, if you can open or manipulate any other window in Citrix via AppleScript, there is (should be) no need to activate it and bring it to foreground; a simple window manipulation in background should be enough to keep its connection alive. Worth experimenting with, anyway.

You might also try scripting any menu command, especially if there are any ‘send’ options, or even just try copy/paste/undo, or opening preferences, or just typing a keystroke within a ‘tell app Citrix’ command, without activating Citrix.

HTH

Ah, I see. Good idea, which I’ve adopted.

The latest version of the script just nudges the Citrix Workspace window around, and because this doesn’t require a GUI kludge the window can stay in the background. Since the actual work takes place in a different window and a different process, I didn’t expect it to work. But so far it seems OK, so thank you for suggesting it.