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
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 launch-emacs-daemon.sh 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.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Replace the after the Program key with your desired command.
launchctl load ~/Library/LaunchAgents/com.user.loginscript.plist and log out/in to test (or to test directly, run
launchctl start com.user.loginscript)
/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:
||String||The name of the job. By convention, the job label is the same as the plist file name, without the .plist extension. Required.|
||String||A path to an executable. Useful for simple launches. At least one of
||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
|The job will be run as the given user, who may (or may not) be the user who submitted it to launchd.|
|Deprecated as of 10.5 with the more powerful
|A boolean flag that defines if a task is launched immediately when the job is loaded into launchd.|
|A boolean flag that defines if a task is launched when a new filesystem is mounted.|
||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
||Array of strings||Watch a filesystem path for changes. Can be a file or folder.|
||Integer||Schedules job to run on a repeating schedule. Indicates number of seconds to wait between runs.|
||Dictionary of integers
Array of dictionaries of integers
|Job scheduling. The syntax is similar to cron.|
||String||The job will be chrooted into this directory before execution.|
||String||The job will be chdired into this directory before execution.|
||String||Keys to determine files for input and output for the launched process.|
||Boolean||Tells the kernel that this task is of a low priority when doing filesystem I/O.|
|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.|