Recently I was trying to change my wallpaper so that it cycled through images. After some reading I found a method that worked, but it required a script to initiate. So how to run a script upon login?

OSX offers a few different startup options for us. First, we have Login Items available within Account Settings of the System Preference pane. We need to rename a shell script so that it ends in a .command file extension to allow the Login Items to execute it. This will run at login, however we will be left with a Terminal prompt visible when the script has completed. Another way is to create an application using Automator, but this also isn’t completely silent.

Preferably we would execute this in the background at user login. OSX manages system and user daemons via a tool called launchd.

Method 1a: .command Shell and Login Items

Create a shell script, but rename the extension as .command.
Use the Login Items (see below) to schedule for login.

Method 1b: Automator and Login Items

This method is probably the simplest, but will only work for one user. Additionally you will have an annoying icon in the taskbar for scripts that run continuously.

Start Automator and select “Application” as your program type

Choose to “Run shell script” (from the Actions/Utilities) and then enter your script into the window.

Save your application somewhere you will remember. You can then test your script by double clicking it.
Once it is working you can add the application to the Login Items so that it runs automatically.

Method 2: Creating a launchctl daemon.

The interface to launchd is a tool called launchctl which allows for loading and unloading daemons into launchd. XML formatted plist files are used to describe operations loaded into launchctl.

A .plist needs to be saved in the ~/Library/LaunchAgents directory.



As you can see the xml dictates that the file should be executed at load, note we no longer need a .command file extension. We will now use launchctl to load our plist file.

launchctl load ~/Library/LaunchAgents/emacs-daemon.plist 

To verify that your script executed correctly lets ask launchctl to show us what is running.

launchctl list | grep emacs

You will likely see two entries, each with three columns. Mine looks like this:

148 - 0x100100e80.anonymous.emacs
- 0 emacs-daemon

The first column is the process id, the first row shows the PID of the emacs daemon our shell script spawned. The second line is the emacs-daemon.plist job entry that executed, returning a status code of zero in the second column – success! With this setup, I can launch the emacsclient process which will attach to the daemon server and launch instantly.

Another example:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "">
<plist version="1.0">

Replace the after the Program key with your desired command.
Save as ~/Library/LaunchAgents/com.user.loginscript.plist
Run launchctl load ~/Library/LaunchAgents/com.user.loginscript.plist and log out/in to test (or to test directly, run launchctl start com.user.loginscript)
Tail /var/log/system.log for error messages.
The key is that this is a User-specific launchd entry, so it will be run on login for the given user. System-specific launch daemons (placed in /Library/LaunchAgents) are run on boot.
If you want a script to run on login for all users, I believe LoginHook is your only option, and that’s probably the reason it exists.

Wiki has a some good information including a property list:

Key Type Description
Label String The name of the job. By convention, the job label is the same as the plist file name, without the .plist extension. Required.
Program String A path to an executable. Useful for simple launches. At least one of Program or ProgramArguments is required.
ProgramArguments Array of strings An array of strings representing a UNIX command. The first string is generally a path to an executable, while latter strings contain options or parameters. At least one of Program or ProgramArguments is required.
UserName String
(defaults to root or current user)
The job will be run as the given user, who may (or may not) be the user who submitted it to launchd.
OnDemand(Deprecated since 10.5) Boolean
(defaults to YES)
Deprecated as of 10.5 with the more powerful KeepAlive option. A boolean flag that defines if a job runs continuously or not.
RunAtLoad Boolean
(defaults to NO)
A boolean flag that defines if a task is launched immediately when the job is loaded into launchd.
StartOnMount Boolean
(defaults to NO)
A boolean flag that defines if a task is launched when a new filesystem is mounted.
QueueDirectories Array of strings Watch a directory for new files. The directory must be empty to begin with, and must be returned to an empty state before QueueDirectories will launch its task again.
WatchPaths Array of strings Watch a filesystem path for changes. Can be a file or folder.
StartInterval Integer Schedules job to run on a repeating schedule. Indicates number of seconds to wait between runs.
StartCalendarInterval Dictionary of integers
Array of dictionaries of integers
Job scheduling. The syntax is similar to cron.
RootDirectory String The job will be chrooted into this directory before execution.
WorkingDirectory String The job will be chdired into this directory before execution.
String Keys to determine files for input and output for the launched process.
LowPriorityIO Boolean Tells the kernel that this task is of a low priority when doing filesystem I/O.
AbandonProcessGroup Boolean
(defaults to NO)
A boolean flag that defines whether subprocesses launched from a task launched by launchd will be killed when the task ends. Useful where a short-lived task starts a long-lived subtask, but may result in zombie processes.