76

How to set custom keyboard shortcuts from terminal for different Linux versions?

Basically I want to know where Linux stores the keyboard shortcut files and how it can be edited.

On my research I found a file ~/.config/compiz-1/compizconfig but it was more or like encrypted when I tried to open it with nano.

1
  • Note, XFCE/Xubuntu systems already have an answer here Apr 30, 2016 at 2:14

10 Answers 10

82

Adding shortcut keybindings in two steps from the command line (14.04+)

Adding custom shortcuts from the command line can be done, but is a bit complicated; it needs to be done in a few steps per keybinding. On the other hand, it is pretty straightforward and can very well be scripted if you somehow want to do it from the command line (that was the question, right?).

Just like in your interface (System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts"), Custom keyboard shortcuts are made from command line in two steps:

  1. create the keybinding by editing (adding to-) the list that is returned by the command:

    gsettings get org.gnome.settings-daemon.plugins.media-keys custom-keybindings
    

    The returned list looks like (if it were only one shortcut currently):

    ['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/']
    

    Apply the edited list by the command:

    gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "[<altered_list>]"
    

    (mind the double quotes)

    N.B. No need to say that the mention in the list (e.g. custom1, custom2) should be a unique one. If you script it, the script should prevent duplicates. In this case the edited list should look like e.g.:

    ['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/']
    

    to add one keybinding: custom1

  2. set its properties:

    • name:

      gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/ name '<newname>'
      
    • command:

      gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/ command '<newcommand>'
      
    • Key combination (for example <Primary><Alt>g):

      gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/ binding '<key_combination>'
      

Useful information can be found here

Example script to set a new custom shortcut

The script below can be used to set a new shortcut key combination from the command line. It can be used with the command (assuming the key combination is available):

python3 /path/to/script.py '<name>' '<command>' '<key_combination>'

An example:

To set a shortcut key combination to open gedit with the key combination Alt+7:

python3 /path/to/script.py 'open gedit' 'gedit' '<Alt>7'

The script:

#!/usr/bin/env python3
import subprocess
import sys

# defining keys & strings to be used
key = "org.gnome.settings-daemon.plugins.media-keys custom-keybindings"
subkey1 = key.replace(" ", ".")[:-1]+":"
item_s = "/"+key.replace(" ", "/").replace(".", "/")+"/"
firstname = "custom"
# get the current list of custom shortcuts
get = lambda cmd: subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")
array_str = get("gsettings get "+key)
# in case the array was empty, remove the annotation hints
command_result = array_str.lstrip("@as")
current = eval(command_result)
# make sure the additional keybinding mention is no duplicate
n = 1
while True:
    new = item_s+firstname+str(n)+"/"
    if new in current:
        n = n+1
    else:
        break
# add the new keybinding to the list
current.append(new)
# create the shortcut, set the name, command and shortcut key
cmd0 = 'gsettings set '+key+' "'+str(current)+'"'
cmd1 = 'gsettings set '+subkey1+new+" name '"+sys.argv[1]+"'"
cmd2 = 'gsettings set '+subkey1+new+" command '"+sys.argv[2]+"'"
cmd3 = 'gsettings set '+subkey1+new+" binding '"+sys.argv[3]+"'"

for cmd in [cmd0, cmd1, cmd2, cmd3]:
    subprocess.call(["/bin/bash", "-c", cmd])

How to use:

Paste the script into an empty file, save it as set_customshortcut.py, run it as explained above.

Some of the mostly used key mentions (found experimentally, looking into the changes the GUI way made into the binding value):

Super key:                 <Super>
Control key:               <Primary> or <Control>
Alt key:                   <Alt>
Shift key:                 <Shift>
numbers:                   1 (just the number)
Spacebar:                  space
Slash key:                 slash
Asterisk key:              asterisk (so it would need `<Shift>` as well)
Ampersand key:             ampersand (so it would need <Shift> as well)

a few numpad keys:
Numpad divide key (`/`):   KP_Divide
Numpad multiply (Asterisk):KP_Multiply
Numpad number key(s):      KP_1
Numpad `-`:                KP_Subtract

etc.

17
  • @JacobVlijm Could you please explain a little on the Key combination part? What does that <primary> tag indicate? And instead of letter g I can replace any letter I think,ryt? Mar 17, 2015 at 9:53
  • @JacobVlijm Thats Okey. But now I have a small issue. When I try to set a new custom say custom1 as u mentioned, I get an error saying expected ',' or ']' to follow array element:. Could you please tell me what could be the issue? Mar 17, 2015 at 11:21
  • This was the command I ran- gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "[['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/']]". Initially I had only custom0. Then I tried to add cutom1 with that. And the error was expected ',' or ']' to follow array element: along with the list I tried to enter. Mar 17, 2015 at 12:32
  • 1
    @VladK ai, xubuntu, could be that xubuntu's shortcuts are (still) set in an xml file. I will have to look. If so, we need to reopen your question, tagged as xubuntu- specific. Apr 22, 2016 at 19:54
  • 1
    Thank you, @JacobVlijm, I've tried it out. And i just find out that my fix was a mistake. There is some really tricky here. custom-keybinding shouldn't contain "s" at the end when inside a schema. But, it should contain "s" when act as key or in path. Otherwise, the "set" command will throw exception. So, please remove the "s" of custom-keybinding in schema. Besides, your python script should update too.
    – e-cloud
    Jul 1, 2016 at 7:53
20

There is plain a simple way of doing that using dconf :

dconf write /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/command "'move-window.sh'"
dconf write /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/binding "'<Primary><Alt>Page_Down'"
dconf write /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/name "'move-window'"

Using gsettings :

gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name "'move-window'"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ binding "'<Primary><Alt>Page_Down'"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ command "'/usr/local/bin/move-window.sh'"

You need to increase number in the custom0part for adding more bindings, ie. custom1, custom2, etc.

To make it permanent, just add it to .bash_profile or a similar script that is ran by login shells. Just don't do it for non-login shells .bashrc because from my experience these dconf and gsettings slow it down significantly. Changing/Adding 30 bindings takes a second ! You don't want this in non-login shell (.bashrc)!

2
  • 1
    Both fail in Ubuntu 18.04. Following @Jacob Vlijm's answer and this community wiki, you need to add custom0 to the custom shortcut list, e.g. with gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['.../custom1/']". I don't know about dconf. Oct 4, 2018 at 15:02
  • Also dconf dump DIR to Dump an entire subpath to stdout. The output is in a keyfile-like format, with values in GVariant syntax. Jun 9, 2019 at 1:41
10

All the custom keyboard shortcuts settings are stored in the dconf database.

You can easily access them with dconf-editor:

sudo apt-get install dconf-editor

Then go to the following dconf path in the editor:

/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/

enter image description here

5
  • Is the location same for all versions of Linux? Or atleast all the versions of Ubuntu? Mar 16, 2015 at 11:42
  • 1
    @AnonymousPlatypus: this is the location of the settings since 14.04 (I can't tell for other distro) Mar 16, 2015 at 11:47
  • 7
    This is graphical method, and not from terminal Mar 17, 2015 at 8:56
  • 1
    What do I do if my media-keys key does not have a custom-keybindings subkey? Jun 27, 2015 at 20:53
  • 3
    The question was about terminal, not GUI, so answer is irrelevant Jan 15, 2019 at 4:10
6

Adding shortcut keybindings from the command line in 12.04

To prevent the accepted answer to become too extensive, posting a separate solution for 12.04.

Until (and including) 12.04, custom keybindings are not stored in the dconf database, but in ~/.gconf/desktop/gnome/keybindings (in an xml file, in subfolders like custom0 etc).

The script below creates the xml file and its containg folder, automatically named correctly.

How to use

  1. Paste the script into an empty file, save it as set_customshortcuts_12.py
  2. Run it with the command:

    python /path/to/set_customshortcuts_12.py <name> <command> <key1> <key2> <key3>
    

    key3 is optional, commands can be for example:

    python /path/to/set_customshortcuts_12.py run_browser firefox Primary 7 
    

    or

    python /path/to/set_customshortcuts_12.py run_texteditor gedit Primary Alt 3 
    

Notes

  • note that the naming of the keys is different from editing gsettings. The keys are named like they show in System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts", as far as I can see.
  • I tested the script on 12.04 under VirtualBox; it needed a log out/in for the changes to take place.
#!/usr/bin/env python
import os
import sys

home = os.environ["HOME"]
name = sys.argv[1]
command = sys.argv[2]
keys = sys.argv[3:]

keyfile = [
    '<?xml version="1.0"?>',
    '<gconf>',
    '\t<entry name="action" mtime="1427791732" type="string">',
    '\t\t<stringvalue>'+command+'</stringvalue>',
    '\t</entry>',
    '\t<entry name="name" mtime="1427791732" type="string">',
    '\t\t<stringvalue>'+name+'</stringvalue>',
    '\t</entry>',
    '\t<entry name="binding" mtime="1427791736" type="string">',
    '\t</entry>',
    '</gconf>',
    ]

if len(keys) == 2:
    keyfile.insert(9, '\t\t<stringvalue>&lt;'+keys[0]+'&gt;'+keys[1]+'</stringvalue>')
else:
    keyfile.insert(9, '\t\t<stringvalue>&lt;'+keys[0]+'&gt;&lt;'+keys[1]+'&gt;'+keys[2]+'</stringvalue>')

n = 0
while True:
    check = home+"/"+".gconf/desktop/gnome/keybindings/custom"+str(n)
    if os.path.exists(check):
        n = n+1
    else:
        newdir = check
        newfile = check+"/"+"%gconf.xml"
        break

os.makedirs(newdir)
with open(newfile, "wt") as shortcut:
    for l in keyfile:
        shortcut.write(l+"\n")
4

Save custom keyboard shortcuts

You can save/backup/export custom shortcuts/keybidings using just dconf and sed

Export

dconf dump / | sed -n '/\[org.gnome.settings-daemon.plugins.media-keys/,/^$/p' > custom-shortcuts.ini # Export

Import

dconf load / < custom-shortcuts.ini # Import

  • To backup you might want to use custom-shortcuts-$(date -I).ini

  • Note that dconf by default reads the user-db and only dumps non-default values (bold in dconf-editor)

  • Only for the added custom shortcuts

  • Based on Ciro's answer (also here)

  • Test if it's working by resetting to defaults this path before importing

    gsettings reset-recursively org.gnome.settings-daemon.plugins.media-keys
    
  • The same way you can backup for example a GNOME Shell extension setup:

    dconf dump / | sed -n '/\[org.gnome.shell.extensions.system-monitor\]/,/^$/p' > extensions-system-monitor-$(date -I).conf
    
3
  • Instead of using sed to grep for media-keys, it's probably better to dump the path directly via dconf dump /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/
    – bk138
    Nov 11, 2020 at 12:29
  • @bk138 Test and compare them. They are not the same. Nov 11, 2020 at 15:55
  • What an under-rated answer! Simple, useful, well explained, well tested. Wish I could upvote you twice.
    – Frank Fu
    Sep 9, 2021 at 13:33
4

I gave up using dconf and tweak tool, I use kbindkeys with ~/.xbindkeysrc and put all my custom keyboard shortcuts, it is much more portable.

  1. First install xbindkeys $ sudo apt install xbindkeys

  2. Generate default .xbindkeysrc on the first time only, on the next time copy them to your home. $ xbindkeys --defaults > ~/.xbindkeysrc

  3. Put your shortkeys on .xbindkeysrc, one example, use Ctrl+Shift+F to skip to the next music on spotify:

    "dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Next"
          m:0x19 + c:41
    
  4. Other ex call gnome-calculator with Ctrl+Alt+Super+c put the line above in .xbindkeysrc:

          Control+Alt+Mod2+Mod4 + c
    

The xbindkeys format is easy, first line is the command and second line is the shortkey.

To discover the shortkey, use: $ xbindkeys -k and press the keys.

Remember to restart xbindkeys when add new shortkeys.

1
  • This does not seem to work in a Wayland session though.
    – bk138
    Nov 11, 2020 at 12:16
1

You can set a new custom shortcut without a python script, by using sed. You just have to set name, binding and action to your choice in the following script:

name="myaction"
binding="<CTRL><ALT>v"
action="/usr/local/bin/myaction"

media_keys=org.gnome.settings-daemon.plugins.media-keys
custom_kbd=org.gnome.settings-daemon.plugins.media-keys.custom-keybinding
kbd_path=/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/$name/
new_bindings=`gsettings get $media_keys custom-keybindings | sed -e"s>'\]>','$kbd_path']>"| sed -e"s>@as \[\]>['$kbd_path']>"`
gsettings set $media_keys custom-keybindings "$new_bindings"
gsettings set $custom_kbd:$kbd_path name $name
gsettings set $custom_kbd:$kbd_path binding $binding
gsettings set $custom_kbd:$kbd_path command $action
1

Wrote a script for that. See below.

See the usage in the creatShortcut invocation.

export nextShortcutId=0
function creatShortcut() {
    name="$1"
    commandToRun="$2"
    binding="$3"
    path="/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom${nextShortcutId}"
    nextShortcutId=$nextShortcutId+1
    dconf write "$path/name" "'""$name""'"
    dconf write "$path/command" "'""$commandToRun""'"
    dconf write "$path/binding" "'""$binding""'"
}

# dconf write /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/binding '"<Primary><Shift>exclam"'
creatShortcut 'copyq show' 'copyq show' '<Primary><Shift>exclam'
creatShortcut 'System Monitor' 'gnome-system-monitor' '<Primary><Alt>m'
creatShortcut 'Suspend' 'systemctl suspend -i' '<Super>d'
creatShortcut 'Volume Up' 'amixer -D pulse sset Master 5%+' '<Super>Page_Up'
creatShortcut 'Volume Down' 'amixer -D pulse sset Master 5%-' '<Super>Page_Down'

overallbindings=""
for ((i = 0 ; i < $nextShortcutId ; i++ ));
do
    overallbindings="$overallbindings, '$customindingPathPrefix$i/'"
done
overallbindings="[${overallbindings:2}]" # Delete the first 2 chars: " ," - space and comma
# echo $overallbindings

# Update the list of bindings for the shortcuts to work
dconf write /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings "$overallbindings"
# dconf write /org/gnome/settings-daemon/plugins/media-keys/custom-keybindings "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom3/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom4/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom5/']"
1
  • It would be perfect if nextShortcutId could auto-detect if old keys are already there (already created by other programs) just to make certain that there are no conflicts. Also, check to see if anything else is bound to the entered keys.
    – Jack G
    Jan 19, 2019 at 1:19
0

I found the answer posted by @JacobVlijm very useful, especially the script. I ported the code to bash. I don’t think this function is perfect, it might contain some bugs, however, it works for me.

Don’t expect me to update this script here. You can find the latest version of the script (and all its revisions) here.

function set_shortcuts(){
    # Usage: set_shortcuts [name] [command] [shortcut]
    unset num i name command shortcut value test value_new
    local name="$1"
    local command="$2"
    local shortcut="$3"
    local value=$(gsettings get org.gnome.settings-daemon.plugins.media-keys custom-keybindings)
    local test=$(echo $value | sed "s/\['//;s/', '/,/g;s/'\]//" - | tr ',' '\n' | grep -oP ".*/custom\K[0-9]*(?=/$)")

    if [ "$(echo "$value" | grep -o "@as")" = "@as" ]; then
        local num=0
        local value_new="['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom${num}/']"
    else
        local i=1
        until [ "$num" != "" ]; do
            if [ "$(echo $test | grep -o $i)" != "$i" ]; then
                local num=$i
            fi
            i=$(echo 1+$i | bc);
        done
        local value_new=$(gsettings get org.gnome.settings-daemon.plugins.media-keys custom-keybindings | sed "s#']\$#', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom${num}/']#" -)
    fi

    gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "$value_new"
    gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom${num}/ name "$name"
    gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom${num}/ command "$command"
    gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom${num}/ binding "$shortcut"
}
0

Here is a Python script which uses PyGObject bindings instead of calling gsettings and parsing the output. When a keybinding of the same name already exists, it replaces it instead of adding a duplicate.

#!/usr/bin/env python3

import sys

from gi.repository import Gio, GLib

media_keys_schema = "org.gnome.settings-daemon.plugins.media-keys"
custom_keybinding_schema = "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding"
keybinding_path_prefix = "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/"

usage_message = '''Usage: {cmd} <name> <command> <binding>

Example:
{cmd} "Open gedit" "gedit" "<Shift><Ctrl>G"
'''.format(cmd=sys.argv[0])


def main():
    if len(sys.argv) < 4:
        sys.exit(usage_message)
    name = sys.argv[1]
    command = sys.argv[2]
    binding = sys.argv[3]

    settings = Gio.Settings(schema=media_keys_schema)
    binding_paths = settings.get_value("custom-keybindings").unpack()  # type: list[str]

    # Figure out which path to use for the new binding. If a binding
    # of the same name already exists, we replace it.
    path = find(binding_paths, name)
    if not path:
        # Use the first gsettings path that doesn't exist yet
        n = 0
        path = keybinding_path_prefix + f"custom{n}/"
        while path in binding_paths:
            n += 1
            path = keybinding_path_prefix + f"custom{n}/"

    # Create the new keybinding
    setting = Gio.Settings(schema=custom_keybinding_schema, path=path)
    setting.set_string("name", name)
    setting.set_string("command", command)
    setting.set_string("binding", binding)
    setting.sync()

    if path not in binding_paths:
        # Add the new keybinding to the list of custom keybindings
        binding_paths.append(path)
        settings.set_value("custom-keybindings", GLib.Variant("as", binding_paths))
        setting.sync()


def find(binding_paths: list[str], name: str) -> str:
    for path in binding_paths:
        setting = Gio.Settings(schema=custom_keybinding_schema, path=path)
        setting_name = setting.get_value("name").unpack()
        if setting_name == name:
            return path


if __name__ == "__main__":
    main()

You must log in to answer this question.

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