5

I need a way to set the following programmatically:

keyboard shortcut

Specifically for shortcuts under Services, if that makes a difference. I'm wondering if this is possible via shell or AppleScript?

EDIT:

I've seen https://stackoverflow.com/questions/7219134/programmatically-add-keyboard-shortcut-to-mac-system-preferences but I'm still not clear on how to set a shortcut for an existing service.

4
  • Might be a different story for Mavericks, but see stackoverflow.com/questions/7219134/… Or there's the long (GUI scripting) way: rngtng.com/2010/10/29/applescript-to-create-keyboard-shortcuts
    – CRGreen
    Dec 29, 2013 at 21:45
  • ... but keep in mind that how Mavericks handles allowing GUI scripting using System Events is different than it used to be, in that you have to allow permission to what runs the script (script application, script editor).
    – CRGreen
    Dec 29, 2013 at 21:53
  • Sorry, didn't see that you already saw that post ... so what have you tried so far?
    – CRGreen
    Dec 29, 2013 at 22:03
  • @CRGreen I've been messing around with several plists trying to find a way to set that specific shortcut. The last link you provided definitely gets me closer. I see that specific property there. Dec 29, 2013 at 23:44

5 Answers 5

5
+100

@chrisKnadler made a good point of using Applescript to do this.

I have used this before in the past but discounted it because of the new problem in Mavericks of preference being cached system wide.

Meaning you could only use the unix command

defaults write...

to get the results to actually change instead of manually changing them or using Applescript plist item commands.

But just now having a look around found a suggestion that you can simply flush the cache and initiate a the system to read the preference again by just calling a

defaults read ...

on the plist file you have change. This does indeed seem to work.

  set theServiceName to "(null) - Print to PDF - runWorkflowAsService"
set libPrefPath to POSIX path of (path to preferences folder from user domain as text)
set PlistFile to "pbs.plist"
set PlistPath to libPrefPath & PlistFile as text
set mainPropertyItem to "NSServicesStatus"
set theKey to "key_equivalent"

tell application "System Events"

    set gp to (get property list item theServiceName of property list item mainPropertyItem of property list file PlistPath)

    tell gp
        --set value of property list item "enabled_services_menu" to true
        --set value of property list item "enabled_context_menu" to true
        set value of property list item theKey to "@^h"

    end tell

end tell

do shell script " defaults read " & quoted form of PlistPath & > /dev/null

You may still need to quite individual apps for them to show the change ( as is normal)


UPDATE: Small change.

Added > /dev/null to the end of the defaults command so only errors are returned.

1
5

You can also use PlistBuddy and defaults:

/usr/libexec/PlistBuddy -c 'Delete NSServicesStatus:"(null) - test2 - runWorkflowAsService"' ~/Library/Preferences/pbs.plist 2>/dev/null;defaults write pbs NSServicesStatus -dict-add '"(null) - test2 - runWorkflowAsService"' '{key_equivalent = "^~@2";}'

The PlistBuddy command is not needed if there is not an existing entry for the service. Replace test2 with the name of the service. Quit and reopen applications to apply the changes.

^~@2 is control-option-command-2. See http://osxnotes.net/keybindings.html.

1
  • I noticed that some of the NSServiceStatus dictionaries have more than one item. I.e enable service menu and context menus. The plistBuddy command would delete all of them. But the defaults is not taking that into account. How important those settings are I am not 100% sure. Not all the dicts have them but may still be enabled. I think it depends how the service is created. I.e by automator or an app.
    – markhunte
    Jan 3, 2014 at 3:30
3

Like most preferences, keyboard shortcuts can be set using "defaults write" commands in the Terminal. Useful if you have lots of shortcuts that you want to define, or if you have more than one Mac to set up. However, the syntax isn't quite the same as the usual commands for setting a key to a value.

For Application-specific commands, use the following:

defaults write com.developer.app NSUserKeyEquivalents -dict-add "Menu Item" -string "@$~^k"

The meta-keys are set as @ for Command, $ for Shift, ~ for Alt and ^ for Ctrl. k in this example is the non-meta-key that you want to use.

For system-wide shortcuts, you can use -g instead of the app identifier, e.g.

defaults write -g NSUserKeyEquivalents -dict-add "Menu Item" -string "@$~^k" 

Note that you'll need to relaunch the app before these will take effect. Also you can see if they've been successful in System Preferences -- which will also need a relaunch to show the changes.

1
  • Does this actually answer the question of adding a shortcut to an existing service menu?
    – markhunte
    Jan 1, 2014 at 13:57
1

Services keyboard shortcuts are a bit different than Application-specific or system-wide shortcuts, although they can be managed using defaults read and defaults write.

The easiest way I've found to figure this sort of thing out is:

defaults read > /tmp/a

Change the keyboard shortcut manually using System Preferences

defaults read > /tmp/b
diff /tmp/[ab]

For the specific case of keyboard shortcuts for Services, in OS-X 10.8 the important domains are "com.apple.ServicesMenu.Services" and "pbs". You need to look at the com.apple.ServicesMenu.Services domain to get the information needed to create an entry in the pbs domain.

For example:

The CFVendedServices key from com.apple.ServicesMenu.Services for "Unarchive to Current Folder is:

{
    NSBundleIdentifier = "cx.c3.theunarchiver";
    NSBundlePath = "/opt/homebrew-cask/Caskroom/the-unarchiver/3.9.1/The Unarchiver.app";
    NSKeyEquivalent = { };
    NSMenuItem = {
        default = "The Unarchiver/Unarchive To Current Folder";
    };
    NSMessage = unarchiveToCurrentFolderWithPasteboard;
    NSPortName = "The Unarchiver";
    NSSendTypes = (
        NSFilenamesPboardType
    );
    NSUUID = <be1910e8 cb1a3475 90854bc5 09f0c0ab>;
},

To create a keyboard shortcut for it, the pbs NSServices key is constructed from the NSBundleIdentifier, NSMenuItem and NSMessage. Example:

defaults read pbs
{
        "cx.c3.theunarchiver - The Unarchiver/Unarchive To Current Folder - unarchiveToCurrentFolderWithPasteboard" =         {
            "enabled_context_menu" = 1;
            "enabled_services_menu" = 1;
            "key_equivalent" = "@~^u";
        };
        "org.vim.MacVim - MacVim/New MacVim Buffer Here - newFileHere" =         {
            "key_equivalent" = "@~^v";
        };
    };
}

Unfortunately, the pbs domain is constructed as nested dictionaries which cannot be handled by defaults write like this simpler cases for other keyboard shortcuts. You'll need to read the entire NSServicesStatus key, insert your new entry, then write it back:

defaults write pbs NSServicesStatus '{
    "com.cocoatech.PathFinder - Path Finder/Show Info - getInfoInPathFinderService" =     {
        "key_equivalent" = "@~^$i";
    };
    "cx.c3.theunarchiver - The Unarchiver/Unarchive To Current Folder - unarchiveToCurrentFolderWithPasteboard" =     {
        "enabled_context_menu" = 1;
        "enabled_services_menu" = 1;
        "key_equivalent" = "@~^u";
    };
    "org.vim.MacVim - MacVim/New MacVim Buffer Here - newFileHere" =     {
        "key_equivalent" = "@~^v";
    };
}'

It may also be necessary to force pbs to flush its cache:

/System/Library/CoreServices/pbs -flush
3
  • I had a play doing this yesterday but did not post it. ( I used a copy of the bps plist to work on)I read in the NSServicesStatus and used sed to change the part I wanted to change. But although that works in changing the dictionary when I wrote it back to the plist, any number or bool would be changed to a string. Have you over come this.
    – markhunte
    Jan 1, 2014 at 18:50
  • From the man pages, it seems like -data would work. I'll give it a shot and see if I cant get something working. This definitely seems like the way to go about it though! ^_^ Jan 2, 2014 at 3:42
  • This seems like another way to do it: discussions.apple.com/thread/4709713?tstart=0 Jan 2, 2014 at 3:47
1

I'm lazy - I use a tool like FastScripts to set global keyboard shortcuts and invoke scripts.

It's free unless you need 11 or more shortcuts defined. Also, the author is a fine gentleman that makes quality software and supports users well beyond what you might expect for a one man shop.

2
  • 1
    This is fabulous, but I actually am attempting to make this shortcut as a part of an installer. I want to try to get this done without using any third party software. Still, this is cool and I'll definitely use it in general. Thanks! (would upvote if I could ^_^) Jan 2, 2014 at 21:07
  • No worries on the vote. I appreciate good comments and tough questions like this more than any 100 votes that come when the internet attention features one question or answer. I'll keep my eyes peeled for other options, but it looks like the easy solutions have all been proposed in other answers here.
    – bmike
    Jan 2, 2014 at 21:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .