34

I was just reading the readline man-page and discovered a bunch of great commands I didn't know about. However, several don't have default key bindings. Is there a way to execute the unbound commands from the shell?

I'm not asking how to bind the command in ~/.inputrc, but instead how to execute it as a "one off" from the shell or in a bash script.

For example, the "dump-variables" command. Is there some command I can feed "dump-variables" to as an argument to have it executed?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
erikcw
  • 441
  • 4
  • 4

3 Answers3

13

I believe what you are looking for is the bind command itself. According to man builtin information running bind <readline-command> allows you to run one-offs, however, I couldn't get it to work like the manual says it should...it kept making keys not work for me; your mileage may vary. I did find the following commands which may be of use to you.

bind -p # Equivalent to dump-functions [machine readable]
bind -P # Equivalent to dump-functions [human readable]

bind -s # Equivalent to dump-macros    [machine readable]
bind -S # Equivalent to dump-macros    [human readable]

bind -v # Equivalent to dump-variables [machine readable]
bind -V # Equivalent to dump-variables [human readable]

Edit Note I would like to point out how annoying it is that it doesn't work like the manual says it should because if you type in bind and then press tab for auto-complete, it shows all of the commands.

sparticvs
  • 2,739
  • 16
  • 22
  • 6
    Re directly calling readline functions: I don't think the manual actually claims that you can do that; in case you're referring to the syntax form bind readline-command: I suspect command there stands for a definition (mapping) line, not a directly callable readline function. The auto-completion is a nicety explicitly preconfigured on some platforms (e.g., Fedora 20), but not on many others; its presence does NOT imply that you can call readline functions directly - it's a fairly "dumb" command-completion spec: the completion works on any argument supplied to bind. – mklement0 Jul 17 '14 at 22:09
  • 2
    +1 for the commands to list functions/macros/variables in effect; it's worth adding -X for shell commands (defined via -x). – mklement0 Jul 17 '14 at 22:12
1

One case where it would make a lot of sense is to "overload" default keystrokes when the line is empty, since many bindings are otherwise meaningless in that case, so lots of the most comfortable strokes are wasted while they could be extremely useful. Something like

alt_n() {
    [ -z $READLINE_LINE ] && do_something_else || execute_default_command
}
bind -x '"\en": alt_n'

otherwise you have to implement the default behavior for all affected bindings, which is fairly easy in some cases, other times not so much, and then it will always be just a hack, prone to weird bugs. An actually working example of this (a lucky coincidence EOF is handled this way), I have stty eof ^B and export IGNOREEOF=2 in my .profile since I kept accidentally closing my sessions. Now <C-B> works just fine in the line editor as cursor left, but on an empty prompt I can press it 3x to terminate.

For me at least it never caused confusion, I'm always aware whether I'm typing a command or just sitting on an empty line. To put it another way, all prompts are implicitly modal, with a lot of wasted hotkey space, it would be really neat to utilize some of it with secondary bindings. Specifically I got here as I want to do this with <M-f> and <M-b> to call pushd +1 and pushd -1 respectively, while keeping the original functionality as well.

I'm even thinking about patching bash, while I'm not a C expert, it couldn't be that hard to extend the bind builtin to accept a single argument and then call the respective readline function. I wonder if someone did that already.

mwy
  • 31
0

I think you overlooked a theoretical problem here.

Executing readline commands makes only sense when reading something. But when a bash command runs, bash usually isn't reading anymore.

Readline is a library and independent from bash. You use it like

// start reading 
char * input = readline("this is the prompt$ ");
// at this place, reading ended
// do something with input
free(input);

Readline can only execute its commands (e.g. forward-word) while readline() is running.

When you enter imaginary_cmd_for_executing_readline forward-word in bash and press enter, then readline() already terminated and therefore cannot execute forward-word anymore. In the source code of bash (or in a custom loadable builtin), you could call the C-function behind any readline command yourself (e.g. rl_forward_word instead of forward-word or rl_get_previous_history instead of previous-history), but these would probably fail, as they read and write to memory areas that might not be usable at that time. And even if they were, you wouldn't get any result, as those only modify the linebuffer and do not actually print anything.

Bash registers some functions in readline that may run while readline() is still running, for instance when

  • a signal arrives (for instance when pressing ctrlc).
  • one of bash's custom readline commands is executed (e.g. shell-expand-line); this only happens when entering a registered key sequence.
  • any key sequence registered with bind -x is entered.

Inside such a hook, you could theoretically execute a readline command. But all of them require keystrokes themselves as a trigger, so there is no benefit in using them over a direct bind. But most of all, there is probably no point in running a readline command on its own when not editing a line, as I explained before.

If you really must, you can temporarily bind any key to the command you want to run, and then send that key stroke automatically from the background to the terminal itself, see Can bash write to its own input stream?.

Socowi
  • 625
  • You can bind a key to a shell function and from that you may want to execute readline commands. At least that's why I came here to look for how to do it. – HKOB Jul 01 '22 at 12:53
  • @HKOB I know: bind -x is already part of my answer :) The problem is a different one, which I tried to explain in detail. – Socowi Jul 01 '22 at 14:54