3

Let's say I have a command called synopsis that takes the name of a command as an argument and prints out its syntax. Now I want to bind the synopsis to a key sequence. How can I pass the name of the command in the current command line to synopsis? This is an example of the desired behavior:

1$ bind -x '"\C-h": synopsis !#:0'
2$ ls -F^H 
<ls syntax print out from synopsis> 
3$ ls -F

Synopsis is invoked in 2, but there's no history expansion. I get the string '!#:0' instead of 'ls'. Nothing seems to work :-/

1

In a function that is bound to a key, you can access and modify the current content of the command line through the variables READLINE_LINE and READLINE_POINT. Proof-of-concept (untested code):

synopsis () (
  set -f
  IFS=$'\t\n\r ;&|'
  words=($READLINE_LINE)
  while [[ "${words[1]}" == @([<>]*|[A-Za-z0-9_]##=) ]]; do
    shift words
  done
  command=${words[1]}
  "$command" --help
)
bind -x '"\C-h": synopsis'

The method you tried with history expansion can't work. History expansion is performed when a command line is executed. When invoking a binding, there's no history to expand.

But your fundamental problem is that your shell isn't cool enough. This feature already exists in zsh, it's called run-help.

bash$ echo 'autoload run-help' >~/.zshrc
bash$ zsh
darkstar% ls -FAlt+h
  • This will work just fine, thank you. One of these days I will start exploring alternative shells. Would be nice to have one based on python improved syntax :-) – Nelson Feb 21 '17 at 10:12
1

One possibility is to use bind without -x, inserting synopsis at the beginning of the line:

bind "'\C-h": "\C-asynopsis \C-j"

That would be ok with one argument. A more complex version would be:

bind '"\C-h": "\C-e !#:0\e^\e\C-] \C-usynopsis\C-j"'

Breaking it down:

'\C-e '    end-of-line
' !#:0'    insert space and 1st argument of current line
'\e^'      history-expand-line
'\e\C-] '  character-search-backward (to last space)
'\C-u'     unix-line-discard (delete to beginning of line)
'synopsis' insert 'synopsis' in front of the first argument
'\C-j'     accept-line

This handles a line with multiple arguments, running synopsis with the first argument only. (I'd also maybe use "\C-xh" or "\eh" instead of "\C-h", which may already be erase/backspace/"backward-delete-char".)

  • Actually, I am using the F1 key for it, which requires a more elaborated solution. Thanks. – Nelson Feb 21 '17 at 10:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.