Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run a shell script periodically on Mac OS X without root permission

I want to start up a file with .sh type or .py on mac os x without using root , I searched in google and found launchctl can help me ,

so i read tutorial and do same in tutorial but it not work for me , [i using mac os x 10.9 x64]

My .plist file [run 1.sh file in every 60second] :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.alvin.crontabtest</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Users/paul/Desktop/1.sh</string>
  </array>
  <key>Nice</key>
  <integer>1</integer>
  <key>StartInterval</key>
  <integer>60</integer>
  <key>RunAtLoad</key>
  <true/>
  <key>StandardErrorPath</key>
  <string>/tmp/AlTest1.err</string>
  <key>StandardOutPath</key>
  <string>/tmp/AlTest1.out</string>
</dict>
</plist>

source of 1.sh:

echo '+' >> /Users/paul/Desktop/worked.txt

I put Run.plist in /Users/paul/Run.plist

and run command from terminal :

launchctl load /Users/paul/Run.plist
Launchctl start com.alvin.crontabtest

commands execute without any error but i not see anything in worked.txt

can anyone help me please ?

like image 331
user3671325 Avatar asked May 26 '14 14:05

user3671325


2 Answers

To clarify: The OP's .plist file itself was perfectly OK - the problem turned out to be inside the shell script invoked (not shown in the question).

On OS X, using .plist files loaded by CLI launchctl and invoked by daemon manager launchd is the preferred way of scheduling (recurring) tasks (see below for more).

Things to note:

  • The format of launchd .plist files is described in man launchd.plist
  • For a .plist file to be loaded every time the current user logs in, it must be placed in ~/Library/LaunchAgents/ (all-users files must be placed in /Library/LaunchAgent/ - requires root privileges).
  • Specifying output-capture files with keys StandardOutPath and StandardErrorPath means that successive invocations append to the files specified, which means that these files keep growing indefinitely, unless managed externally.
  • Re troubleshooting: @Grady Player's advice applies: launch Console.app and look for com.apple.launchd.peruser entries - a failure to invoke the command specified in the .plist would show there.

@ghoti's answer describes a general Unix alternative to launchd, cron (typically used on Linux):

As for how cron relates to OS X: @ghoti asks:

Any particular reason you don't want to use a normal crontab?

On OS X, man crontab advises (emphasis added):

Although cron(8) and crontab(5) are officially supported under Darwin [OS X], their functionality has been absorbed into launchd(8), which provides a more flexible way of automatically executing commands. See launchctl(1) for more information.

The bottom line is this:

  • If you come from a *nix background, you may be more comfortable with continuing to use cron and crontab, assuming:
    • you're aware of the fact that additional background tasks, scheduled via launchd, may exist.
    • you're aware of cron's limitations and can work with/around them.
  • Otherwise, on OS X:
    • many third-party apps use the native launchd feature and thus specify periodic background tasks via .plist files in /Library/LaunchAgents (for all users) or ~/Library/LaunchAgents (for the current user).
    • If you want to centralize management of background tasks to these locations and/or you want to take advantage of the increased flexibility that launchd provides, go with specifying background tasks via .plist files evaluated by launchd.

Adding simple cron tasks is probably simpler than creating .plist files for launchd, but 3rd-party utilities such as Lingon 3 can help with the latter.

Also, there are subtle differences in how cron tasks are invoked vs. per-user launchd tasks: for instance, the former do not allow user interaction via AppleScript, whereas the latter do.


Case in point re increased flexibility of launchd: the OP, in a follow-up comment, asks for the task to run every 30 seconds:

  • The minimum interval for cron tasks is 60 seconds, necessitating the workaround in @ghoti's answer.

  • By contrast, with the launchd .plist file, changing <key>StartInterval</key><integer>60</integer> to <key>StartInterval</key><integer>30</integer> is enough.

like image 141
mklement0 Avatar answered Nov 16 '22 15:11

mklement0


Any particular reason you don't want to use a normal crontab?

% echo "* * * * * /Users/paul/Desktop/1.sh" | crontab -

This command should add a cron job that runs once per minute.

NOTE that this command will also replace any crontab that you already have. The crontab - command should be used with caution, as a short-cut.

If you want to edit an existing crontab, so as to avoid obliterating previously set jobs, you can use crontab -e. (If it launches vim and you don't know how to use vim, you can exit by hitting ESC:q!Enter and then go find editor documentation.)

If you want instructions on how to edit crontabs, type man crontab at your shell. If you want syntax information on the crontab file, man 5 crontab will show you that.

Enjoy!


UPDATE: (per comments)

To run your job every 30 seconds requires a simple hack. Cron only runs jobs on a per minute basis, so to run things every 30 seconds, you can have two jobs, one of which has a 30 second delay. For example:

  #Mn Hr Da Mo DW Command
  *   *  *  *  *   /Users/paul/Desktop/1.sh
  *   *  *  *  *   sleep 30; /Users/paul/Desktop/1.sh

Hope this helps.

like image 9
ghoti Avatar answered Nov 16 '22 16:11

ghoti