Shell scripts are pretty useful, but they can be hard to learn, especially when you’re just working with novel examples that serve no practical function.
Here’s a practical example, which can change my mouse sensitivity:
xinput --set-prop $(xinput list | grep "mini keyboard Mouse" | sed 's@^[^0-9]*\([0-9]\+\).*@\1@') 'libinput Accel Speed' 0.4
This can work on your system too, the only things you’d have to change are that 0.4
(how sensitive you want the mouse to be) and the word “Mouse” (which I’ll explain in a bit).
Let’s first look at the shell syntax at work here, mainly two concepts are being used here.
First, the |
character is called a pipe, which can redirect the output of one command into the input of another. This powerful feature of most Unix shells allows me to daisy-chain all of these programs together——in this case, xinput
, grep
, and sed
——and get the desired output without having to write a whole new program from scratch.
Then there’s this $(command)
syntax——which I’ve just learned is called command substitution. It runs the command contained inside of it, allowing you to use the output of a pipe as a single argument of a command——in this instance, I’m using that whole big pipeline inside of the command substitution. I think you can also do this by surrounding the command with backtick characters——something like `command`
——but I prefer this syntax a bit more as it makes it clearer what’s going on and I believe it is also supported by more shells.
(Aside: man, is it hard to to put backticks in a markdown code snippet!)
First, we invoke the program xinput
, which is taking 4 arguments here: --set-prop
, our big pipeline inside of that command substitution line, the name of the option we want to change (’libinput Accel Speed’), and the acceleration value, 0.4
.
For the uninitiated, xinput
can be used to “list available input devices” or “change input device settings” (source). So here, we’re using it to set our input device. However, how do we tell which input device we’re supposed to be setting? Well, there’s a unique number that identifies it that can be found through the output of xinput list
(the other use of xinput), which for me outputs the below:
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ mini keyboard Mouse id=10 [slave pointer (2)]
⎜ ↳ mini keyboard Consumer Control id=12 [slave pointer (2)]
⎜ ↳ SONiX USB DEVICE Keyboard id=18 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Power Button id=7 [slave keyboard (3)]
↳ Sleep Button id=8 [slave keyboard (3)]
↳ mini keyboard id=9 [slave keyboard (3)]
↳ mini keyboard System Control id=11 [slave keyboard (3)]
↳ SONiX USB DEVICE id=17 [slave keyboard (3)]
↳ SONiX USB DEVICE Wireless Radio Control id=19 [slave keyboard (3)]
↳ mini keyboard Consumer Control id=20 [slave keyboard (3)]
↳ SONiX USB DEVICE Keyboard id=23 [slave keyboard (3)]
Through trial and error, I’ve figured out that the right line for the mouse that I want to change is the “mini keyboard Mouse” with “id=10”, but then there’s another problem: this number can randomly change on each startup! So, this is why that command substitution has to be there, to account for this change.
So now we have to parse this output programmatically in order to get the number we want so we can adjust our mouse sensitivity consistently across power cycles. So let’s put it all in that command substitution and start piping in a bunch of programs to get what we want.
Let’s look at just that part again:
xinput list | grep "mini keyboard Mouse" | sed 's@^[^0-9]*\([0-9]\+\).*@\1@'
So, we’re first calling xinput list
, which we’ve explained already, but now we have to parse the damn thing!
Enter grep
, a program I probably use every day. By itself, it takes input like grep [string-to-search-for] [filename]
and prints the line containing that string we are looking for. But if we pipe the output of xinput list
into it, we can search for a certain line, then we can get just the line we’re interested in——not what we want (we want just the id number), but a step closer.
For my unique identifier, I use the name, so for me that’s “mini keyboard Mouse”. You might have a different string you should use to search for, go check with xinput list
.
Anyway, now we’ve got the line, but how can we get that elusive id number out of there? There are a number of ways. You can probably use awk
, xargs
, or cut
to solve this problem, however, I’m most familiar with sed
, so I used that.
Now, I’m not actually that familiar with sed
, but I’m familiar enough with it to know what it can do. So I searched “find first number in a string in bash” and found this link, which describes exactly what I want to do. sed
uses regular expressions to parse text, and I’m only good enough at regexes to roughly estimate what they’re doing, so I’ll skip the explanation on this one. (I’ve been meaning to learn them well enough to construct more complex ones on my own for a long time though).
Regardless though, that sed
line leaves us with our id number, which makes xinput
a happy program because that’s exactly what it’s second argument was looking for!
And that’s it, my mouse sensitivity is adjusted and I am a happy man. Thanks, bash
(or whatever shell you were using, I think this should be POSIX sh
compliant (I only know for sure that it works with bash
and ksh
)).
Site Licensing Site last updated: 2024-12-08