How to navigate between applications the efficient way: a simple bash script


I like using keyboard shortcuts in order to control my computer efficiently. Here is a keyboard way of opening applications and of raising the window of the application I want to work with next. This is a summary of how it works:

  • For every application you use regularly, choose an alphabetic character (like for example „a“ for audio player, „e“ for editor, and „w“ for LibreOffice Writer).
  • If you press the windows key plus such a character key, the corresponding application is opened if no instance of the application is running. Otherwise, the existing application window is raised and given focus.
  • This makes having the application you want „ready to hand“ easy: If the next thing you want to do is using your editor, press windows+e. If after a while you want to browse your music collection, press windows+a. You do not have to think about whether applications are running or not. You do not have to use the menu or the mouse. Instead, you press a keyboard shortcut and can start using the chosen application.


I currently use the Debian GNU/Linux distribution, but the approach described here should work with pretty much every GNU/Linux distribution. You will need a window manager that allows to define custom keyboard shortcuts (and which is EWMH/NetWM compatible, but the most common ones are).

In the above summary, I choose the „windows“ key as an example of a modifier key. (The „windows“ key is called „Super“ by the X window manager.) I use the „windows“ or „Super“ key and reserve it for controlling things at the window manager level. You could as well use another modifier key. (For example, if you do not need CapsLook, you could use xmodmap to make it a „Hyper“ key and use this.) You definitely do not want to use the „Control“ of „Alt“ key at the window manager level as it is often used within applications. Below, I use „MODIFIER“ (instead of „window“ or „Super“) to refer to the specific modifier key that is chosen for this approach (i.e. for keyboard shortcuts at the window manager level).

One last note about using the „windows“ key: If your window manager uses the key to open a menu, this might conflict with using it as a modifier key. I therefore set the keyboard shortcut for opening the menu to „windows+return“, so that only pressing the „windows“ key does nothing.

Setting things up

Follow these steps for setting up the keyboard shortcuts:

  1. Install wmctrl, a small application to interact with a X window manager.
  2. Copy the bash script below into your editor, save it as in the directory of your choice and make it executable.
  3. In your window manager settings, define keyboard shortcuts for running the script with different parameters (one shortcut and parameter for every application or command you use regularly):
    • Make MODIFIER+a run /path/to/ a (i.e., „a“ is passed as a parameter for the script!).
    • Make MODIFIER+b run /path/to/ b (now, „b“ is given as the parameter).
    • Continue with all the letters which you want a shortcut for. You can also use other keys like Esc, comma or whatever, but use „esc“, „comma“ (not „,“) as parameter for the script.
  4. Edit the script according to your preferences so that it uses the applications you use. Try out every new or edited keyboard shortcut both with the application not running and running.

The bash script

This is how the bash script looks like in principle:

#!/bin/bash ## This script is meant to be called by keyboard shortcuts. In order ## to find out the class of a running application window, open a ## terminal and run "wmctrl -lx". if [ "$1" == "a" ]; then if ! wmctrl -x -a "rhythmbox.Rhythmbox"; then rhythmbox & fi elif [ "$1" == "c" ]; then if ! wmctrl -x -a "libreoffice.libreoffice-calc"; then localc & fi elif [ "$1" == "v" ]; then if ! wmctrl -x -a "vlc.Vlc"; then vlc & fi elif [ "$1" == "w" ]; then if ! wmctrl -x -a "libreoffice.libreoffice-writer"; then lowriter & fi fi exit
Code-Sprache: Bash (bash)

Let’s see what the script does, so that you can edit it to meet your preferences.

  • Bash will interpret $1 as the parameter with which the script is called.
  • There are two levels of if statements. At the outer level, the script checks whether the parameter equals given character strings (mostly just one character long). If so, the corresponding nested code is run, which is the if statement at the inner level. This outer level is convenient because we can thus define the commands for all the keyboard shortcuts in a single file. (It’s easier to edit a single file than to create/edit one for each keyboard shortcut.)
  • At the inner level, the if statement checks whether the exit code of a given call of wmctrl is 1 (indicating failure). The wmctrl command tries to raise a specific application window and give it focus. If the exit code is 1 (i.e. wmctrl could not find a window of this application), then the command after then is executed, i.e. the application is opened (started). If it is not 1, then the wmctrl command was successful, because the application was already running. Because wmctrl was already called within the if statement, it did raise the window and there is nothing more to do.
  • Each call of wmctrl has a character string as a parameter. This must be the WM_CLASS name of the window. Use wmctrl -lx to see the class names of the opened windows.

Edit the script to add applications or commands you want to run by using a keyboard shortcut.

Adjustments for specific applications

For some applications or commands, you may want to adjust the corresponding part of the script a bit. Here are some examples:

This is the part of my script for running the Emacs editor:

elif [ "$1" == "e" ]; then if ! wmctrl -x -a "emacs.Emacs"; then emacs -title Emacs -f my-start-function-in-emacs & sleep 4 wmctrl -F -r Emacs -b add,maximized_vert,maximized_horz fi
Code-Sprache: Bash (bash)

Emacs is called with the arguments -title (for setting a windows title) and -f (for running a specific lisp function at startup). Then, after 4 seconds (to make sure the window is open), the Emacs window is maximized.

For the email client Thunderbird, I have two shortcuts and sections in the script:

elif [ "$1" == "m" ]; then if ! wmctrl -x -a "Mail.Thunderbird"; then thunderbird & fi elif [ "$1" == "M" ]; then if ! wmctrl -x -a "Msgcompose.thunderbird"; then if ! wmctrl -x -a "Mail.Thunderbird"; then thunderbird & fi fi
Code-Sprache: Bash (bash)

The first section checks for the class „Mail.Thunderbird“ which is the class of the main thunderbird window. I can thus use MODIFIER+m to raise that window. Additionally, I can use MODIFIER+SHIFT+m to raise the (first) email composing window, i.e. the window in which you write the email. If there is no such window, MODIFIER+SHIFT+m will raise the main thunderbird window or open Thunderbird if it is not yet running.

For terminals I also use two shortcuts:

elif [ "$1" == "t" ]; then gnome-terminal --geometry 71x25+600+400 & elif [ "$1" == "T" ]; then if ! wmctrl -F -a "fullterminal"; then gnome-terminal --title=fullterminal & sleep 2 wmctrl -F -r fullterminal -b add,maximized_vert,maximized_horz fi
Code-Sprache: Bash (bash)

For most applications I usually only use one instance. But MODIFIER+t does not call wmctrl to check whether an existing window can be raised. Instead, the shortcut will always open an additional terminal (in a relatively small window). However MODIFIER+SHIFT+t will check for a window with the title „fullterminal“ and raise it or create a new one. This is a maximized „main“ terminal that I can easily raise; but only one instance is opened.

I use copyq as a clipboard manager. Here is the corresponding section of the script:

elif [ "$1" == "y" ]; then copyq show
Code-Sprache: Bash (bash)

Normally, copyq runs in the background without any window. The command copyq show however opens the copyq window so that I can easily select an entry from the clipboard and paste it.

Here is how I can easily control the Rhythmbox audio player. Many other players also allow pausing or skipping the currently played song by a command.

elif [ "$1" == "down" ]; then rhythmbox-client --play-pause elif [ "$1" == "left" ]; then rhythmbox-client --previous elif [ "$1" == "right" ]; then rhythmbox-client --next
Code-Sprache: CSS (css)

As you might have guessed I have set keyboard shortcuts for MODIFIER + the arrow keys in order to control the audio player.

Here is the last example. I use the cinnamon window manager and this is how to open the restart/shutdown dialogue and preselect „power off“:

elif [ "$1" == "esc" ]; then cinnamon-session-quit --power-off &
Code-Sprache: CSS (css)

That’s it. Hope you find this methodology of opening applications or raising their window as useful as I do. If you like, let me know what you think about it.