Skip to content
Advertisement

Bash completion sometimes meshes up my terminal when the completion function reads a file

So I’ve been having a problem with some cli programs. Sometimes when I kill the running process with Ctrl+C, it leaves the terminal in a weird state (e.g. echo is turned off). Now that is to be expected for many cases, as killing a process does not give it a chance to restore the terminal’s state. But I’ve discovered that for many other cases, bash completion is the culprit. As an example, try the following:

  1. Start a new bash session as follows: bash --norc to ensure that no completions are loaded.
  2. Define a completion function: _completion_test() { grep -q foo /dev/null; return 1; }.
  3. Define a completion that uses the above function: complete -F _completion_test rlwrap.
  4. Type exactly the following: r l w r a p Space c a t Tab Enter (i.e. rlwrap cat followed by a Tab and then by an Enter).
  5. Wait for a second and then kill the process with Ctrl+C.
  6. The echo of the terminal should have not been turned off. So if you type any character, it will not be echoed by the terminal.

What is really weird is that if I remove the seemingly harmless grep -q foo /dev/null from the completion function, everything works correctly. In fact, adding a grep -q foo /dev/null (or even something even simpler such as cat /dev/null) to any completion function that was installed in my system, causes the same issue. I have also reproduced the problem with programs that don’t use readline and without Ctrl+C (e.g. find /varTab| head, with the above completion defined for find).

Why does this happen?

Edit: Just to clarify, the above is a contrived example. In reality, what I am trying to do, is more like this:

_completion_test() {
    if grep -q "$1" /some/file; then
        #do something
    else
        #do something else
    fi
}

For a more concrete example, try the following:

_completion_test() {
    if grep -q foo /dev/null; then
        COMPREPLY=(cats)
    else
        return 1
    fi
}

But the mere fact that I am calling grep, causes the problem. I don’t see why I can’t call grep in this case.

Advertisement

Answer

Well, the answer to this is very simple; it’s a bug:

This happens when a programmable completion function calls an external command during the execution of a completion function. Bash saves the tty state after every successful job completes, so it can restore it if a job is killed by a signal and leaves the terminal in an undesired state. In this case, we need to suppress that if the job that completes is run during programmable completion, since the terminal settings at that time are as readline sets them for line editing. This fix will be in the release version of bash-4.4.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement