Conditioning launchd scheduled job on file existence?

We have a scheduled job that we want to run on both our macOS and Linux hosts.

On Linux, with systemd we can just use this:

ConditionPathExists=/etc/this_file_must_exist
ConditionPathExists=!/etc/skip_if_this_exists

and the job will only execute if the first file exists, and the second file is absent (so we can pause the scheduled job from running, e.g. if we're testing a new version).

Is there an equivalent on macOS? I've seen others achieve a similar result by using Bash and just running `[[ -f /etc/this_file_must_exist ]] && run_my_job`` but this seems... not to be the ideal way.

launchd.plist's manpage (https://www.manpagez.com/man/5/launchd.plist/ -- official link seems down) has KeepAlive but that's for keeping the service up, not preventing it from starting.

Thanks!


Solution 1:

Short answer: Nope.

launchd can start jobs at scheduled times (with the StartCalendarInterval key), when particular files are modified (WatchPaths), or when files appear in a particular directory (QueueDirectories), but it doesn't have a way to test existence of a specific file, nor to combine conditions the way you want to.

So... something like a bash wrapper is what you're looking at. You can either write a bash script and run that with a StartCalendarInterval trigger, or use the ProgramArguments array to embed a miniature script directly in the plist:

<key>ProgramArguments</key>
<array>
    <string>/bin/bash</string>
    <string>-c</string>
    <string>[[ -e /etc/this_file_must_exist ]] && [[ ! -e /etc/skip_if_this_exists ]] && exec /path/to/my_job</string>
</array>

Note: I'd recommend running your job from bash with exec; that makes the job replace the shell (rather than running as a subprocess), so launchd can monitor and manage it more directly.