Bash 当完成函数读取文件时,完成有时会与我的终端啮合
Bash completion sometimes meshes up my terminal when the completion function reads a file
所以我在使用某些 cli 程序时遇到了问题。有时,当我用 Ctrl+C 终止 运行 进程时,它会使终端处于一种奇怪的状态(例如回声被关闭).现在这在很多情况下都是意料之中的,因为终止进程不会给它机会恢复终端的状态。但我发现对于许多其他情况,bash 完成是罪魁祸首。例如,尝试以下操作:
- 启动一个新的 bash 会话,如下所示:
bash --norc
以确保不加载任何补全。
- 定义一个补全函数:
_completion_test() { grep -q foo /dev/null; return 1; }
.
- 定义一个使用上述函数的补全:
complete -F _completion_test rlwrap
.
- 准确输入以下内容:r l w r a p Space c a t Tab Enter(即
rlwrap cat
后跟 Tab,然后是 Enter)。
- 稍等片刻,然后使用 Ctrl+C.
终止进程
- 终端的回显应该没有关掉。所以如果你输入任何字符,它不会被终端回显。
真正奇怪的是,如果我从完成函数中删除看似无害的grep -q foo /dev/null
,一切都会正常进行。事实上,向我系统中安装的任何完成函数添加 grep -q foo /dev/null
(或什至更简单的东西,例如 cat /dev/null
)会导致同样的问题。我还重现了不使用 readline
且没有 Ctrl+C 的程序的问题(例如 find /var
Tab| head
,为 find
).
定义了上述补全
为什么会这样?
编辑:澄清一下,上面是一个人为的例子。实际上,我想做的更像是这样:
_completion_test() {
if grep -q "" /some/file; then
#do something
else
#do something else
fi
}
有关更具体的示例,请尝试以下操作:
_completion_test() {
if grep -q foo /dev/null; then
COMPREPLY=(cats)
else
return 1
fi
}
但是我调用 grep
的事实导致了问题。我不明白为什么在这种情况下我不能调用 grep
。
您只是错误地实现了完成功能。请参阅 manual
-F function
The shell function function is executed in the current shell environment. When it is executed, is the name of the command
whose arguments are being completed, is the word being completed,
and is the word preceding the word being completed, as described
above (see Programmable Completion). When it finishes, the possible
completions are retrieved from the value of the COMPREPLY array
variable.
例如下面的实现:
_completion_test() { COMPREPLY=($(cat /dev/null)); return 1; }
不会破坏终端。
关于您最初的问题,为什么您的完成函数会中断终端,我已经对 strace
进行了一些研究,我看到有 ioctl
个带有 -echo
参数的调用。我假设当你用 Ctrl+C 终止它时,ioctl
和 echo
参数就不会被调用以恢复原始状态。输入 stty echo
将返回 echo
。
好吧,这个问题的答案很简单;这是一个 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.
所以我在使用某些 cli 程序时遇到了问题。有时,当我用 Ctrl+C 终止 运行 进程时,它会使终端处于一种奇怪的状态(例如回声被关闭).现在这在很多情况下都是意料之中的,因为终止进程不会给它机会恢复终端的状态。但我发现对于许多其他情况,bash 完成是罪魁祸首。例如,尝试以下操作:
- 启动一个新的 bash 会话,如下所示:
bash --norc
以确保不加载任何补全。 - 定义一个补全函数:
_completion_test() { grep -q foo /dev/null; return 1; }
. - 定义一个使用上述函数的补全:
complete -F _completion_test rlwrap
. - 准确输入以下内容:r l w r a p Space c a t Tab Enter(即
rlwrap cat
后跟 Tab,然后是 Enter)。 - 稍等片刻,然后使用 Ctrl+C. 终止进程
- 终端的回显应该没有关掉。所以如果你输入任何字符,它不会被终端回显。
真正奇怪的是,如果我从完成函数中删除看似无害的grep -q foo /dev/null
,一切都会正常进行。事实上,向我系统中安装的任何完成函数添加 grep -q foo /dev/null
(或什至更简单的东西,例如 cat /dev/null
)会导致同样的问题。我还重现了不使用 readline
且没有 Ctrl+C 的程序的问题(例如 find /var
Tab| head
,为 find
).
为什么会这样?
编辑:澄清一下,上面是一个人为的例子。实际上,我想做的更像是这样:
_completion_test() {
if grep -q "" /some/file; then
#do something
else
#do something else
fi
}
有关更具体的示例,请尝试以下操作:
_completion_test() {
if grep -q foo /dev/null; then
COMPREPLY=(cats)
else
return 1
fi
}
但是我调用 grep
的事实导致了问题。我不明白为什么在这种情况下我不能调用 grep
。
您只是错误地实现了完成功能。请参阅 manual
-F function
The shell function function is executed in the current shell environment. When it is executed, is the name of the command whose arguments are being completed, is the word being completed, and is the word preceding the word being completed, as described above (see Programmable Completion). When it finishes, the possible completions are retrieved from the value of the COMPREPLY array variable.
例如下面的实现:
_completion_test() { COMPREPLY=($(cat /dev/null)); return 1; }
不会破坏终端。
关于您最初的问题,为什么您的完成函数会中断终端,我已经对 strace
进行了一些研究,我看到有 ioctl
个带有 -echo
参数的调用。我假设当你用 Ctrl+C 终止它时,ioctl
和 echo
参数就不会被调用以恢复原始状态。输入 stty echo
将返回 echo
。
好吧,这个问题的答案很简单;这是一个 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.