Skip to main content

Keystroke injection for comfort

·7 mins
Kristof Kovacs
Author
Kristof Kovacs
Software Architect & DevOps Consultant

Hello, I’m Kristof, a human being like you, and an easy to work with, friendly guy.

I've been a programmer, a consultant, CIO in startups, head of software development in government, and built two software companies.

Some days I’m coding Golang in the guts of a system and other days I'm wearing a suit to help clients with their DevOps practices.

The pendulum of customizing our environment #

Probably most people (but certainly me) go through different "phases" in our lifetime regarding the customization of our work environment. Sometimes we over-customize, then we go back to basics.

Normally, when I start something new, I like to use things as they are. No customization, no addons, no plugins, no nothing. To get a feel of how things were intended.

But soon, of course, one starts to customize the environment, making small and big adjustments... Which is usually a good thing: it allows one to be more efficient, effective, to work faster, to enjoy the work more.

My first computer was a Commodore. Then, I got a "cartridge", and with that came wonderful new commands and modes. I loved it, but then it was uncomfortable when friends' computers haven't had them.

Commodore cartridge Dragonsden

Then there was DOS. Instead of Norton Commander, I used Volkov Commander, because it had some features that fit me better (no, I can't actually remember what those were). Which made me very efficient, but made other people's PCs more uncomfortable for me.

Volkov Commander

Then on Linux, for a long while I was using zsh while I everyone used bash. Zsh people anyway tend to overcustomize things, which culminates in ohmyzsh. Just have a look at that prompt! (You can be sure that you are overdoing customization when you need a custom font for your stuff to work well.)

oh my zsh

It was easy because the computers I worked on were mine, and the servers were "my" servers. (I administered them.)

But sooner or later one always loses the customizations that were built up over months, even years of careful tuning. Maybe one changes computers or workplaces. Maybe as a consultant, one has to work on servers that are not his own. Then it's "back to basics" time, barely any customization, just the minimal stuff that affects one's efficiency the most. An Occam's razor moment.

And over time, as the pendulum swings back and forth, one starts to build up a small repository of actually meaningful customizations for his person.

Which brings us to today.

Keystroke injection for fun and profit #

So my situation today is that while most of the time I physically use my own MacBook, all the remote servers, VMs, containers come and go, and most of the time I don't "own" them: the very least it's our team that works on them, and in my consultant role they often even belong to other teams. Some are even carefully set up to someone else's liking.

So I can't really force my own beloved .bashrc, .vimrc, .screenrc, etc on them. (For one, I use vi keybindings with everything, which makes me super effective in the command line, but really hamstrings "non-vim" peasants people.)

Of course none of the systems have zsh, so I'm back on bash. (Which, turns out, can be made surprisingly comfortable, despite its bad defaults.)

Which presents a dilemma:

How can I work on remote systems as efficiently as I know I can do, while letting other people live with their mistakes preferences?

Furtunately, there is a solution.

In my own terminal, I use tmux (and on remote machines screen, but that's a story for another time). So I set my local terminal up so it can send a minified version of the relevant parts of my dotfiles as if I would be typing them into the remote shell. (Injecting it.)

Here is how this looks like in the scary, minified form, which honestly, makes it look like one was just hacked. (Not exactly what you would want to show up in the .bash_history file! 😆 But it does not, more about that later.)

kkrc bash inject

But in reality, it's only the minification that makes it look scary – actually, here you can see the same code in a more readable form, with comments. Just a small excerpt:

# Alias grep to use colors only if not on busybox
[ -L $(type -p grep) ] || alias grep="grep --color"

# Shell options.
# globstar: To have "**" as in zsh
# cdspell, dirspell: Spell checking on tab expansion
# checkwinsize: Set LINES and COLUMNS
# cmdhist: Save multi-line commands as one command
shopt -s globstar cdspell dirspell checkwinsize cmdhist

# No swapfile, no viminfo. Always use UTF-8. Enable mouse.
alias vim='vim -n -i NONE "+set nobackup noswapfile encoding=utf8 mouse=a"'

# Configure readline
bind 'TAB:menu-complete'
bind 'set menu-complete-display-prefix on'

It's the same code, but the minified version gets "typed" and executed on the remote system in less than a second, never touching any files, and not affecting anything except my own, current shell session. Nothing persistent. Fully ephemeral. Very Buddhist. 😀

It's beautiful. And the last few commands even leave some quick info regarding the remote system (OS, uptime, memory, virtualization, screens running, etc) that orient me.

And I do the same injection to some other tools, where I have settings that are important enough. For example, vim, and GNU screen. These are worth injecting for me, but not everything does. (For example, I've mostly given up on tuning psql and mysql, I just use them as they are.)

Some small details for whoever cares #

You can see the small script that builds the bash inject here. The minification itself is done by a tool made by someone else, unfortunately abandoned. (It's not without fault, but it was easier to fix the mistake with a sed command than to debug the minifier.)

The only line maybe worth explaining is the following:

printf 'i\x7f HISTCONTROL=ignoreboth\r clear;' >../inject-bash.txt
  • The i\x7f part (literally an i and a <backspace>) are important in case the remote bash would already be in vi mode. This forcefully puts vi-mode into "insert" mode, while not causing any problems if we are in "emacs" mode (just typing a letter and deleting it).
  • The HISTCONTROL=ignoreboth\r part and the surrounding two spaces (important!) take care of not messing up the remote .bash_history. The ignoreboth setting tells bash that lines starting with a space are not to be stored in the history. If we are lucky, and this was already the case, then the HISTCONTROL line, which starts with a space itself, is not stored either. If it was not so, then only that line gets into the history, and not the following messy "line noise".
  • The clear; comes at the start of the script, but since all is "typed" as one line, it actually gets executed after the screen was messed up by the "line noise" part. Clearing the screen has the advantage of the ugly part not lingering long on the screen, thus not scaring any onlookers. Clearing at the front has the advantage that any potential error messages that might result from the ugly part (which happens occasionally: on old operating systems, non-compatible shells, etc) remain visible, and if necessary, debuggable.

By the way, besides "injecting" configurations, I do use tmux's send-keys for other shortcuts too: to quickly install things on temporary VMs, to drop an .editorconfig file, etc. For example, here's a one-liner from my .tmux.conf, that:

  1. finds out the current latest golang version from the web,
  2. checks the remote system's architecture,
  3. based on the previous two, downloads the right .tar.gz for the given system,
  4. extracts it,
  5. and soft-links the executables into /usr/local/bin for easy use.
set -s command-alias[114] setup_golang="send-keys ' (cd /tmp; FNAME=\$(curl -s https://go.dev/dl/ | grep -om 1 \"go[[:digit:].]\\+linux-$(dpkg --print-architecture)\\.tar\\.gz\"|head -1); wget -nc \"https://go.dev/dl/\$FNAME\"; cd /usr/local/; sudo tar zxf \"/tmp/\$FNAME\"; sudo find /usr/local/go/bin/ -type f -executable -exec ln -s \"{}\" /usr/local/bin/ \\; );' enter"

Yes, one could do this manually every time. But for me, I can do all this in just literally four keystrokes. (Could be less if I needed it to.)

I'm not advocating that everyone should work like this. This method – keystroke injection from my terminal emulator – works very well for me for years now, and for me it is the perfect balance between customization and using things "stock". What is yours?

P.s.: And if I'm in an even more restriced environment, where I can't even use my own laptop to connect and there is no Internet, the absolute minimum customization for me is that I type set -o vi. I can't live without that.