1

I'm running Raspbian on my Raspberry Pi and I want to be able to run a shell script when I press a key on my keyboard. As far as I know AutoHotkey isn't available for Linux, otherwise I'd do it in AHK.

For those of you familiar with AHK, I simply want to do this:

SetWorkingDir, /scripts/
Numpad1::
Run toggle.sh
Return

What is the Linux solution?

I don't use any desktop environment. Is it possible to have this hotkey active before logging in? If not, I can leave it logged in.

3
  • Should it always work? or after logging in? Logging in using tty1 or so? or is there any desktop environment you log into? Apr 5, 2019 at 22:28
  • It should always work and I don't use a desktop environment. Is it possible to have a script run before login? If not I can leave it logged in Apr 5, 2019 at 22:47
  • I was planning to use a numpad, as I can put stickers over the keycaps to describe what each key actually does. Also I plan for this keypad to be firmly mounted in one place all the time, which isn't possible for many remotes, as they have rounded shapes and stuff. And also weird small buttons which can't be replaced so you have to remember that the "ok" button is toggle.sh and the "2" button is reload.sh.... Not ideal but I'll look into it as an option Apr 7, 2019 at 11:41

2 Answers 2

1

I have access to my friend's Raspbian (which is not entirely up to date). Still, I don't want to mess with the configuration of the device that is not mine, especially if it involves keyboard support; so the following answer is based on my work in Debian 9. I used Raspbian only to confirm the needed packages are available there. Some details may vary between Debian and Raspbian.


Preparation

You need these packages:

  • lirc. Its common usage is to read from controllers (like infra red remotes) and send various commands to various compatible programs (like multimedia players). It can also run arbitrary system commands (e.g. scripts) via irexec executable, this would be your case. In my Debian the lirc package provides irexec.service but in (outdated) Raspbian it seems to provide only the executable, not the service. I will address this later in the answer. And then there is…
  • inputlirc, zeroconf LIRC daemon using input event devices (like keys on a regular keyboard). We will use its daemon rather than the large lircd.service (lirc package is still required because of irexec we want to use).
  • input-utils, utilities useful to set things up.

Install them:

sudo apt-get install lirc inputlirc input-utils

If you want to use a specific keyboard (rather than all available keyboards), find out which device it is:

sudo lsinput

In my case the device I wanted to use was a combo device, it registered as /dev/input/event8 and /dev/input/event9. To see which one is the right device I used

sudo input-events 9        # for /dev/input/event9

I pressed keys I wanted to use and observed the output. I repeated for event8. It turned out my device passes "normal" keys via event9 and multimedia keys via event8.

There's no guarantee the same device will get the same number in the future, after reboot. The OS however supplies symlinks in /dev/input/by-id. Examine them:

ls -l /dev/input/by-id

These paths shouldn't change, you should prefer them during further configuration.


Configuration

inputlircd

inputlircd is the daemon from the inputlirc package. We want to use it to read from keyboard. I think the large LIRC daemon is not really required, so disabling lircd.service may seem like a good idea. However there are some dependencies which would run LIRC anyway. Rerouting them would make this answer overly complicated, there's no point; so let's keep it as it is.

In my Debian there is /etc/init.d/inputlirc file. Upon examining I see it uses options from /etc/default/inputlirc. Set the right values there. My /etc/defalut/inputlirc now looks like this:

# Options to be passed to inputlirc.
EVENTS="/dev/input/by-id/usb-1ea7_2.4GHZ_Keyboard___Mouse_Combo-event-mouse /dev/input/by-id/usb-1ea7_2.4GHZ_Keyboard___Mouse_Combo-if01-event-kbd"
OPTIONS="-m 0"

Note I used two devices in a form /dev/input/by-id/something, although to monitor all connected keyboards /dev/input/input* should be right (it was the out-of-the-box setting). You may want to use -m, -n and/or other options. Read man 8 inputlircd.

After saving the file, enable and (re)start the service:

systemctl enable inputlirc.service
systemctl restart inputlirc.service

and check if it's running:

systemctl status inputlirc.service

irexec.service

In my Debian I have /lib/systemd/system/irexec.service. My friend's Raspbian lacks the file (even though lirc package is installed and irexec is available). If you need to create it manually, this is its original content from my Debian:

[Unit]
Documentation=man:irexec(1)
Documentation=http://lirc.org/html/configure.html
Documentation=http://lirc.org/html/configure.html#lircrc_format
Description=Handle events from IR remotes decoded by lircd(8)

[Service]
; user=lirc
; group=lirc

; Hardening opts, see systemd.exec(5).  Doesn't add much unless
; not running as root. If these are applicable or not depends on
; what commands irexec.lircrc invokes.
;
; NoNewPrivileges=true
; MemoryDenyWriteExecute=true
; PrivateTmp=true
; ProtectHome=true
; ProtectSystem=full


Type=simple
ExecStart=/usr/bin/irexec /etc/lirc/irexec.lircrc

[Install]
WantedBy=multi-user.target

The file is owned by root:root and the permissions are 644. I guess it's good to add After=inputlirc.service and Requires=inputlirc.service in the [Unit] section. I'm not an expert in dependencies, so this may be sub-optimal or not enough.

If you consult man 1 irexec, you will see this /etc/lirc/irexec.lircrc path that appears above is the config file. Put this snippet in the config file:

begin
    prog   = irexec
    button = KEY_MUTE
    config = beep -r 5
end

and invoke

systemctl restart irexec.service

to make irexec start to react to the mute key. Instead of beep you can use cd /scripts/ && ./toggle.sh. Now it you press the key, the tool will pass the command to sh to execute.

To know the key name (e.g. KEY_MUTE) you can peek what inputlircd passes through the socket:

socat UNIX-CONNECT:/var/run/lirc/lircd STDOUT

Press keys you want to use and note the output. Names to use are in the third column.


Notes

  • In my Debian the default socket for inputlircd and irexec is /var/run/lirc/lircd (and /run/lirc/lircd is the same because of the symlink /var/run -> /run). If in your case the two tools use different sockets, make them use a single one, it's crucial. There are options for this, see the respective manuals.
  • For versions up to 0.9.1 irexec used to wait until the executed program terminated. […] This is not required in 0.9.2+ which cannot wait for command completion.

    (source)
    This means you are able to run multiple instances of the script in parallel, just press the key fast enough. If this shouldn't happen, the command you run or the script itself must detect its previous instance(s) and wait or terminate. I would use a lockfile for this. The script itself may be the lockfile, like this:

    config = cd /scripts/ && flock -w 1 ./scriptA.sh ./scriptA.sh
    
  • The solution doesn't suppress the "normal" action of the chosen key. You may be concerned about the key littering the login input on TTY1, or even password, if Enter gets hit. This may lead to login attempts. Workarounds:

    • switch to unused TTY;
    • mask getty@tty1.service (see this; I haven't tested this though, I don't know if the OS doesn't switch to a used one automatically);
    • run some custom command instead (like in this answer, also not tested by me).
  • Pay attention as what user the relevant services run. There are also ProtectHome= and ProtectSystem= options. These (along with few others) will limit what your script can do. And if some filesystem is encrypted or unmounted, the script won't be able to use it until it gets mounted properly (e.g. if your home directory gets decrypted as late as at the moment you log in, the script won't be able to interact with it until you log in).

2
  • This answer is excellent and it is my pleasure to upvote. I'm stepping through it now to try to get a single key foot pedal (which registers as a keyboard) to do what I want it to do.
    – fivedogit
    Jan 28, 2020 at 15:12
  • 1
    Got this working. beep was no good, so I used "aplay -D 'hw:0' /usr/share/sounds/alsa/Front_Left.wav" to get audio when I pressed KEY_SPACE on a standard keyboard. Had to unmask irexec.service, too. Also had to "sudo cp /etc/lirc/lirc_options.conf.dist /etc/lirc/lirc_options.conf" or the daemon won't start.
    – fivedogit
    Jan 28, 2020 at 16:12
0

Install xbindkeys, run xbindkeys -k and press your key. Then copy down the response and paste it into the .xbindkeysrc file that exists in your root directory, in the form command, line break, then pre-copied key code. This should execute the command when the xbindkeys daemon is running (Hint - you can run this by adding the command xbindkeys to your startup).

More documentation on this is here

I use this daily to launch Terminal using the ThinkVantage button on my Thinkpad - it works with other keys too, like running a custom script with ctrl + numpad_minus key.

1
  • The tool requires X server, $DISPLAY and such; doesn't it? Apr 7, 2019 at 18:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.