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.

4
  • 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
    Commented May 25, 2012 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. Commented May 25, 2012 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
    Commented May 25, 2012 at 15:22
  • @Levon: And most of my scripts are in Perl, Commented May 25, 2012 at 15:29

4 Answers 4

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.

2
  • Thanks, that's exactly what I was looking for. This could well be the final nail in the coffin of my tcsh usage. Commented Apr 19, 2012 at 23:00
  • @KeithThompson Or you could switch to zsh, which does this easily, and more generally nabbed pretty much every feature of tcsh and bash. Commented Apr 19, 2012 at 23:17
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 ;)

1
  • Very cool, I'll have to play with this. A minor point: I'd use $(command) rather than backticks. Commented Apr 28, 2020 at 21:50
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"
0

To add some tricks you can do with the accepted answer, you can even use something like

bind -x '"\C-g": echo -en "\e[6t"'

to send current window to bottom, or other escape codes invented by xterm to center the window, place window at specific pixel location, etc.

See also vttest(1) (it turns out you can learn a lot of fancy escape sequences just playing around with vttest) and https://invisible-island.net/xterm/ctlseqs/ctlseqs.html

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .