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:
ctrl-a) moves the cursor to the beginning of the 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 (
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:
noneif you don't want to hear a terminal ring again.
onif you want to see completions in different colors to indicate their file type.
editing modeset to either
videpending on your religious affiliation.
onif you want to make your life easier with arrow keys.
onif you want to expand the tilde to a full path.
history-sizecontrol the number of lines stored in the history buffer (and saved to .bash_history).
horizontal-scroll-modeset to on will cause a command-line to scroll across the bottom of the screen instead of wrap to a new line.
keymapsets the overall keymap for key binding commands, options include
keyseq-timeoutsets the timeout duration in milliseconds for Readline to wait when reading an ambiguous key sequence.
onif 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-linemoves the cursor to the beginning of the current line.
end-of-linemoves the cursor to the end of the current line.
forward-charmoves the cursor forward one character.
backward-charmoves the cursor backward one character.
forward-wordmoves the cursor forward to the end of the next word.
backward-wordmoves the cursor backward to the start of the current or previous word.
clear-screenclears the screen and redraws the current line at the top of the screen.
redraw-current-lineredraws the current line in its current location.
previous-historymoves back through the history list, displaying the previous command.
next-historymoves forward through the history list, displaying the next command.
beginning-of-historydisplays the first entry of the history list.
end-of-historydisplays the last entry of the history list, the current line being entered.
history-search-backwardmoves backward through the history list, displaying lines that match your partially typed command.
history-search-forwardmoves 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-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.
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
PageDown just like the rest of us mortals.
"\e[1~": beginning-of-line "\e[4~": end-of-line "\e[5~": history-search-backward "\e[6~": history-search-forward "\e[3~": delete-char