Run external command in inputrc mapping, without modifying current line

Solution 1:

Bash

This is possible, but not through inputrc. (This file belongs to the Readline library, which – although mostly known from Bash – is actually a generic text input library used by many different programs and doesn't really deal with shell-specific things like "running a command".)

Instead, this is a custom action provided by Bash itself (not available in other Readline-using programs), and has to be defined using Bash's bind builtin, specifically, the -x option:

bind -m vi-command -x '"\C-h": tmux select-pane -L'

Zsh

Zsh's ZLE is different, in that it is specifically part of the actual zsh shell, but it also doesn't have a straightforward "execute command" option. Instead, you have to define a custom ZLE widget, which can only call a shell function, and then bind that to a key.

  1. Define a function that runs the thing.

    tmux-go-left() { tmux select-pane -L; }
    

    When running an interactive program, its stdin must be redirected from /dev/tty (otherwise it'll read EOF and immediately quit), and zle -I must be called to "invalidate" the screen contents when the program exits (otherwise the cursor will be in the wrong location). For example:

    open-htop() { htop </dev/tty; zle -I; }
    
  2. Define a widget that calls the function.

    zle -N tmux-go-left
    zle -N open-htop
    

    If the function name is not provided, it's assumed to be the same as the widget name.

  3. Bind a key to the widget.

    bindkey -a '\C-h' tmux-go-left
    bindkey -v '\C-p' open-htop
    

    It seems that -v selects Vi insert mode; -a selects Vi command mode.

Sources:

  • man zshzle
  • https://stackoverflow.com/questions/57539180/why-is-interactive-command-breaking-when-using-zsh-widget-to-execute-it