10

(Background: I'm a long-time tcsh user, gradually transitioning to bash, and trying to find equivalents for some useful tcsh-specific features.)

In tcsh, I can define a key binding that executes an external command. For example, given:

bindkey -c ^Gu uptime

I can type "Control-G u" in tcsh, and it will execute the uptime command. I don't have to type Enter, the command doesn't appear in my history, and I can do it in the middle of an input line (I find the latter particularly useful for certain commands).

bash has a similar key binding mechanism via the GNU readline library, with bindings specified in $HOME/.inputrc (or elsewhere). But after reading the info readline documentation, I don't see a way for a key binding to execute an external command.

The closest thing I've figured out is to add something like this to my .inputrc file:

"\C-gu": "uptime\n"

but that doesn't execute the command; rather, it acts as if I had typed uptime followed by the Enter key. The command appears in my history (that's ok), and it works only on an empty line; if I type "echo control-Gu", then it prints uptime rather than executing the command.

Another minor drawback is that the binding affects other commands that use GNU readline, such as the Perl debugger.

Is there a way to simulate the effect of tcsh's bindkey -c in bash, by mapping a key sequence to the execution of a specified external command?

If it matters, I'm using bash 4.2.24 on Ubuntu 12.04 beta 2.

Keith Thompson
  • 21,782
  • 6
  • 48
  • 55
  • As a long-time user of tcsh myself, I'm curious why you are making the transition (other than perhaps the prevalence of bash shells/users) – Levon May 25 '12 at 14:08
  • @Levon: bash is installed by default more commonly than tcsh. bash is better for scripting, and it's nice to use the same language for scripting and interactive use. – Keith Thompson May 25 '12 at 15:13
  • All true .. maybe I'll make the switch eventually too (most of my scripts are actually in Python :-) .. but I see your point for sure. Thanks. – Levon May 25 '12 at 15:22
  • @Levon: And most of my scripts are in Perl, – Keith Thompson May 25 '12 at 15:29

3 Answers3

12

Not all bash line editing is controlled from ~/.inputrc; much of it is configured via the bind builtin. In this case, you want something like

bind -x '"\C-gu":uptime'

in your ~/.bashrc.

phemmer
  • 70,657
  • 19
  • 188
  • 223
geekosaur
  • 31,429
  • 5
  • 79
  • 58
3

The other answer provides a solution, that executes the command in a new line. This is useful in some cases, but most of the time my workflow is such, that either I want to

  • Insert the result of an inline execute command in my current command-line (example below)
  • Execute the command truly in the background, no output, no changes in current command line

I use these two principles much more often than the variant @geekosaur showed. Here is an example:

bind '"\C-gd":"\C-u`date +%Y%m%d%H%M`\e\C-e\C-a\C-y\C-e"'

This bind CTRL-gd to kill the existing command (saving it in the cut buffer), insert some shell command, in this case date +%Y%m%d%H%M for a nice timestamp, execute that in-place, and then paste the saved command back to the front of the command-line.

I have a bunch of commands, that output some information from my system, that I regularly use in other command lines, think get_lan_ip, get_gw_ip, get_gw_pubip, get_ns_ip, get_root_block_dev, get_email_addr, get_phone_number and so on. Basically like programmable abbreviations! and all of them one key-stroke behind \C-g away only.

The other very useful use-case for me is calling functions inline, that do not produce output, but silently trigger a background action, like mediaplayer_next, mediaplayer_pause, speakerphone_answer, ... for when I want to trigger things without leaving my terminal, and without losing sight of my current windows contents for even a moment ;)

Alex Stragies
  • 5,857
  • 2
  • 32
  • 56
2

To add onto the accepted answer, you don't really need to use bind -x. You can still add it to your inputrc, but instead of \n at the end of your sequence, it has to be the binding for accept-line. For example:

"\C-m": accept-line
"\C-gu": "uptime\C-m"

Since it causes uptime to be entered into the commandline, it'll be in your history, which is either an advantage or disadvantage depending on your use-case.

Here ^M is the carriage return on Mac and Windows, but it doesn't really matter what the sequence is. For example the following works too:

"\xxacceptline": accept-line
"\C-gu": "uptime\xxacceptline"