Ivan Kovacevic's pointers, especially the superuser.com link, are helpful; since at least OS X 10.9.2, your options for creating run-at-login scripts are:
Note: The methods are annotated with respect to whether they are:
- specific to a given user ("[user-SPECIFIC]"); i.e., the installation must be performed for each user, if desired; scripts are typically stored in a user-specific location, and root (administrative) privileges are NOT required for installation.
- effective for ALL users ("[ALL users]"); i.e., the installation takes effect for ALL users; scripts are typically stored in a shared location and root (administrative) privileges ARE required for installation.
The scripts themselves will run invisibly, but - with the exception of the com.apple.loginwindow
login-hook method - you can open applications visibly from them; things to note:
There is no guarantee that any such application will be frontmost, so it may be obscured by other windows opened during login.
If you want to run another shell script visibly, simply use open /path/to/your-script
, which will open it in Terminal.app
; however, the Terminal window will automatically close when your script terminates.
Automator [user-SPECIFIC]:
com.apple.loginwindow
login hook [ALL users - DEPRECATED, but still works]:
If you have admin privileges, this is the easiest method, but it is DEPRECATED, for a variety of reasons (security, limited to a single, shared script, synchronous execution); Apple especially cautions against use of this mechanism as part of a software product.
- Place your script, e.g.,
Test.sh
, in a shared location - e.g., /Users/Shared
- and make sure it is executable (chmod +x /Users/Shared/Test.sh
).
From Terminal.app
, run the following:
sudo defaults write com.apple.loginwindow LoginHook /Users/Shared/Test.sh
Note:
The script will run as the root user, so exercise due caution.
Among the methods listed here, this is the only way to run a script as root.
There's only one system-wide login hook.
- Note that there's also a log-OUT hook,
LogoutHook
, which provides run-at-logout functionality - unlike the other approaches.
The login-hook script runs synchronously before other login actions, and should therefore be kept short.
- Notably, it runs before the desktop is displayed; you cannot launch applications from the script, but you can create simple interactions via
osascript
and AppleScript snippets (e.g., osascript -e 'display dialog "Proceed?"'
); however, any interactions block the login process.
The script runs in the context of the root user and he username of the user logging on is passed as the 1st argument to the script.
- The script runs with the default
"C"
locale.
$PATH
is fixed to /usr/bin:/bin:/usr/sbin:/sbin
, which notably does NOT include /usr/local/bin
- The working dir. is
/
.
launchd agents:
launchd
-agent-executed scripts can be installed for a SPECIFIC user OR for ALL users - the latter requires administrative privileges.
While using launchd
is Apple's preferred method, it's also the most cumbersome, as it requires creating a separate *.plist
configuration file.
On the upside, you can install multiple scripts independently.
- Note:
- No specific timing or sequencing of
launchd
scripts is guaranteed; loosely speaking, they "run at the same time at login"; there is even no guaranteed timing between the user-specific and the all-user tasks.
- The script runs with the default
"C"
locale.
$PATH
is fixed to /usr/bin:/bin:/usr/sbin:/sbin
, which notably does NOT include /usr/local/bin
- The working dir. is
/
by default, but you can configure it via the .plist
file - see below.
- The script-file path must be specified as a full, literal path (e.g.,
/Users/jdoe/script.sh
; notably , ~
-prefixed paths do not work.
- For a description of all keys that can be used in
*.plist
configuration files, see man launchd.plist
.
- Both user-specific and all-users tasks run as the current user (the user logging on).
launchd [user-SPECIFIC]:
- Note:
Lingon 3
($5 as of early 2014) is a GUI application that facilitates the process below, but only for user-specific scripts.
- Place your script, e.g.,
Test.sh
, in your home folder, e.g., /Users/jdoe
Create a file with extension .plist
in ~/Library/LaunchAgents
, e.g., ~/Library/LaunchAgents/LoginScripts.Test.plist
, by running the following in Terminal.app
:
touch ~/Library/LaunchAgents/LoginScripts.Test.plist
Open the file and save it with the following content:
<?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>
<!-- YOUR SELF-CHOSEN *UNIQUE* LABEL (TASK ID) HERE -->
<string>LoginScripts.Test.sh</string>
<key>ProgramArguments</key>
<array>
<!-- YOUR *FULL, LITERAL* SCRIPT PATH HERE -->
<string>/Users/jdoe/Test.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
The <!-- ... -->
comments indicate the places to customize; you're free to choose a label, but it should be unique - ditto for the .plist
filename; for simplicity, keep the label and the filename root the same.
From Terminal.app
, run the following:
launchctl load ~/Library/LaunchAgents/LoginScripts.Test.plist
Note that, as a side effect, the script will execute right away. From that point on, the script will execute whenever the CURRENT user logs on.
- It is not strictly necessary to run
launchctl load
-- since, by virtue of the file's location, it will be picked up automatically on next login -- but it's helpful for verifying that the file loads correctly.
launchd [ALL users]
- Place your script, e.g.,
Test.sh
, in a SHARED location, e.g., /Users/Shared
Create a file with extension .plist
in /Library/LaunchAgents
(requires admin privileges), e.g., /Library/LaunchAgents/LoginScripts.Test.plist
, by running the following in Terminal.app
:
sudo touch /Library/LaunchAgents/LoginScripts.Test.plist
Open the file and save it with the following content (make sure your text editor prompts for admin privileges on demand; alternatively, use sudo nano /Library/LaunchAgents/LoginScripts.Test.plist
):
<?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>
<!-- YOUR SELF-CHOSEN *UNIQUE* LABEL (TASK ID) HERE -->
<string>LoginScripts.Test.sh</string>
<key>ProgramArguments</key>
<array>
<!-- YOUR *FULL, LITERAL* SCRIPT PATH HERE -->
<string>/Users/Shared/Test.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
The <!-- ... -->
comments indicate the places to customize; you're free to choose a label, but it should be unique - ditto for the .plist
filename; for simplicity, keep the label and the filename root the same.
From Terminal.app
, run the following:
sudo chown root /Library/LaunchAgents/LoginScripts.Test.plist
sudo launchctl load /Library/LaunchAgents/LoginScripts.Test.plist
Note that, as a side effect, the script will execute right away. From that point on, the script will execute whenever ANY user logs on.
- It is not strictly necessary to run
launchctl load
-- since, by virtue of the file's location, it will be picked up automatically on next login -- but it's helpful for verifying that the file loads correctly.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…