3

In a bash script I'm reading some user input with read. Now I want to provide the possibility to auto-complete the input by hitting the Tab key.

Easy example: Let's say, the user shall type in a name from a limited domain. In the script I have an array containing all valid names, those should be included in the auto-completion-suggestions.

I already tried something with programmable completion, but I want the script to be portable, i.e., everything should be in this very script. Something comparable would be mysql - if you type SELECT * FROM and the hit Tab it shows all available tables in the database (and actually all columns).

Should work on Mac OS X.

3
  • 3
    Possible duplicate of Writing a tab completion script for bash
    – Guido
    Commented Apr 23, 2016 at 19:24
  • Thanks, but this is exactly what i don't want!
    – Jakob Benz
    Commented Apr 23, 2016 at 19:29
  • 1
    Agreed. @Guido I don't think it's a duplicate. The "duplicate" is about command line completion. This one is about tab completion at a read prompt. Similar, quite possibly, but definitely different. Commented Apr 23, 2016 at 23:32

1 Answer 1

3

Use rlwrap, a readline wrapper.

From man rlwrap

rlwrap runs the specified command, intercepting user input in order to provide readline's line editing, persistent history and completion.

and from the Debian rlwrap package description:

This package provides a small utility that uses the GNU readline library to allow the editing of keyboard input for any other command. Input history is remembered across invocations, separately for each command; history completion and search work as in bash and completion word lists can be specified on the command line.

A very simple example script:

#! /bin/bash

ynm=(Yes No Maybe)

reply=$(rlwrap -S 'Do you want to continue? ' -H ~/.jakob.history
        -e '' -i -f <(echo "${ynm[@]}") -o cat)

echo "reply='$reply'"

This uses rlwrap's one-shot mode to run cat (to get stdin) but accept only one line of input. -o cat is rlwrap's recommended substitute for read.

Command-line history is stored in ~/.jakob.history, and the completion items are in the bash array $ynm.

rlwrap expects a file as the argument to the -f option. Fortunately, we can use process subsitution <(echo "${ynm[@]}") to supply an array rather than a file.

-i enables case-insensitivity for completions.

The -e '' stops rlwrap from appending a space after a successful completion (so that $reply ends up containing, e.g., 'Maybe' rather than 'Maybe ' with a trailing space)

If you want a default already pre-typed on the input line, you could use the -P or --pre-given option - e.g. add -P Yes to the rlwrap command in the example script above. The user would only have to hit Enter to accept or backspaces or Ctrl-U to erase the default (as is normal for readline in emacs mode).

see man rlwrap for details and more options. e.g. you can enable filename completion with -c or --complete-filenames.

Check if rlwrap is packaged for your distro (it is for Debian and probably Ubuntu/Mint/etc, at least) before downloading and compiling the source.

1
  • Thanks! I've been trying a lot of things now, but it won't work. Could you provide a minimal example?
    – Jakob Benz
    Commented Apr 24, 2016 at 14:02

You must log in to answer this question.

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