Configuring Readline on SmartOS
One of the more annoying differences I noticed in the switch from Linux to SmartOS was while connecting to a terminal via PuTTY: The former supports using the Home and End keys to navigate to the beginning and end of a command-line buffer while the latter does not, instead spitting tilde characters into the command-line wherever you happen to have the cursor.
My Google-fu was weak and unfocused (see: distracted) at the time, so I'd usually just spend a few minutes fruitlessly searching for an answer until deciding to give up and focus on the original problem I was working on, trying my best to ignore the fact that I couldn't zip back and forth across a multi-row command like I was used to being able to on Linux.
It finally got bad enough that I committed myself to finding a solution before I moved back to my original task. This blog post is the culmination of my findings. Thanks to geedoubleya and rofrol from Stack Exchange for their work on this answer which set me on the right track for researching this article.
Bonus: Most of this guide should apply well to any modern readline installation!
Bash and Readline
What I was used to were features of GNU Bash and more specifically GNU Readline, the library that provides the line-editing and history functionality for Bash. Fortunately, with SmartOS, my problem was one of misconfiguration, not of missing software, as both Bash and Readline are already installed in most (if not all?) SmartOS images.
You can actually confirm this by testing against the default Readline key bindings. The relevant ones are as follows:
beginning-of-line
(ctrl-a
) moves the cursor to the beginning of the line.end-of-line
(ctrl-e
) moves the cursor to the end of the line.
And while we could just stick with those, where's the fun in limiting ourselves to that? Readline supports reconfiguration via a simple configuration file (~/.inputrc
or /etc/inputrc
).
Readline configuration
Readline configuration file syntax supports two common definition structures: Variables can be set with the form set <variable> <value>
and key bindings can be defined with the form keyname: function-name
or "key sequence": function-name
forms.
Setting variables will alter the run-time behavior of Readline. Variables that I found interesting include:
bell-style
set tonone
if you don't want to hear a terminal ring again.colored-stats
set toon
if you want to see completions in different colors to indicate their file type.editing mode
set to eitheremacs
(default) orvi
depending on your religious affiliation.enable-keypad
set toon
if you want to make your life easier with arrow keys.expand-tilde
set toon
if you want to expand the tilde to a full path.history-size
control the number of lines stored in the history buffer (and saved to .bash_history).horizontal-scroll-mode
set to on will cause a command-line to scroll across the bottom of the screen instead of wrap to a new line.keymap
sets the overall keymap for key binding commands, options includeemacs
(default),emacs-standard
,emacs-meta
,emacs-ctlx
,vi
,vi-move
,vi-command
andvi-insert
.keyseq-timeout
sets the timeout duration in milliseconds for Readline to wait when reading an ambiguous key sequence.mark-symlinked-directories
set toon
if you want symlinks to directories to have a/
appended to their names.
Key bindings will allow you to map specific functions or macros to keys. Functions that I found interesting and relevant include:
beginning-of-line
moves the cursor to the beginning of the current line.end-of-line
moves the cursor to the end of the current line.forward-char
moves the cursor forward one character.backward-char
moves the cursor backward one character.forward-word
moves the cursor forward to the end of the next word.backward-word
moves the cursor backward to the start of the current or previous word.clear-screen
clears the screen and redraws the current line at the top of the screen.redraw-current-line
redraws the current line in its current location.previous-history
moves back through the history list, displaying the previous command.next-history
moves forward through the history list, displaying the next command.beginning-of-history
displays the first entry of the history list.end-of-history
displays the last entry of the history list, the current line being entered.history-search-backward
moves backward through the history list, displaying lines that match your partially typed command.history-search-forward
moves forwards through the history list, displaying lines that match your partially typed command.
There's a bunch of other stuff in the documentation (like conditional blocks and a BUNCH of other bindable functions) that while I found interesting, didn't have a really practical place to apply it while writing this article. You may want to give it a proper read if you'd like to know more.
When testing your configuration, you can prompt a reload by using the key sequence Ctrl-X
Ctrl-R
, instead of logging out and back in again.
Guest Zone configuration
As mentioned before, the system-wide configuration file is located at /etc/inputrc
which can be overridden by individual users with a file at ~/.inputrc
. If you tend to switch roles into different users often, I recommend using the system-wide configuration file, as those configuration directives will be applied to all users on the system.
Global Zone Configuration
Since SmartOS Global Zones are refreshed on reboot, we'll need to set up a transient SMF manifest to load our settings into the global zone each time it boots up.
Download the above manifest to /opt/custom/smf/readline.xml
and copy your inputrc file to /usbkey/config.inc/inputrc
. This will ensure that your SmartOS global zone will copy /usbkey/config.inc/inputrc
from persistent memory into the ramdisk (/etc/inputrc
) upon each boot.
My Inputrc
While a vim user, I prefer not having to deal with that editor's intricacies while I'm in bash. Also, I tend to ssh from Windows, PuTTY handles my copy buffer, so all I really want is for readline to handle is command-line navigation. I originally wanted to do a pretty straightforward interface where basic arrow keys provided navigation and modifier keys (Alt, and Ctrl) increased magnitude, but it turns out that arrow keys in a terminal are just "Metafied" ASCII, and pressing the Alt key while pressing one of those keys is just the equivalent of a normal ASCII sequence. If I were to attempt these bindings, I would end up with normal ASCII in my terminal: Not exactly what I was looking for.
So, for now, I'm stuck using Home
, End
, PageUp
and PageDown
just like the rest of us mortals.
/etc/inputrc:
"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": history-search-backward
"\e[6~": history-search-forward
"\e[3~": delete-char