[英]Changing tab-completion for read builtin in bash
The current tab-completion while "read -e" is active in bash seems to be only matching filenames:在 bash 中“read -e”处于活动状态时,当前的制表符补全似乎只匹配文件名:
read -e
[[TabTab]]
abc.txt bcd.txt cde.txt
I want the completion to be a set of strings defined by me, while file/dir/hostname-completion etc. should be deactivated for the duration of "read -e".我希望完成是我定义的一组字符串,而文件/目录/主机名完成等应该在“read -e”期间停用。
Outside of a script在脚本之外
complete -W 'string1 string2 string3' -E
works well, but i cant get this kind of completion to work inside a script while using "read -e".效果很好,但我无法在使用“read -e”时在脚本中完成这种工作。
Although it seems like a reasonable request, I don't believe that is possible.虽然这似乎是一个合理的要求,但我认为这是不可能的。
The existing implementation of the read
builtin sets the readline completion environment to a fairly basic configuration before calling readline to handle -e
input. read
内置函数的现有实现在调用 readline 处理-e
输入之前将 readline 完成环境设置为相当基本的配置。
You can see the code in builtins/read.def
, in the edit_line
function : it sets rl_attempted_completion_function
to NULL
for the duration of the call to readline
.您可以在
builtins/read.def
中的edit_line
函数中看到代码:它在调用readline
期间将rl_attempted_completion_function
设置为NULL
。 readline
has several completion overrides, so it's not 100% obvious that this resets the entire completion environment, but as far as I know this is the function which is used to implement programmable completion as per the complete
command. readline
有几个完成覆盖,所以这不是 100% 明显重置整个完成环境,但据我所知,这是用于根据complete
命令实现可编程完成的功能。
With some work, you could probably modify the definition of the read
command to allow a specific completion function instead of or in addition to the readline standard filename completion function.通过一些工作,您可能可以修改
read
命令的定义以允许特定的完成功能,而不是 readline 标准文件名完成功能或除此之外。 That would require a non-trivial understanding of bash internals, but it would be a reasonable project if you wanted to gain familiarity with those internals.这将需要对 bash 内部结构有重要的了解,但如果您想熟悉这些内部结构,这将是一个合理的项目。
As a simpler but less efficient alternative, you could write your own little utility which just accepts one line of keyboard input with readline
and echoes it to stdout.作为一种更简单但效率较低的替代方法,您可以编写自己的小实用程序,它只接受一行带有
readline
的键盘输入并将其回显到标准输出。 Then invoke read
redirecting its stdin to your utility:然后调用
read
将其标准输入重定向到您的实用程序:
read -r < <(my_reader string1 string2 string3)
(That assumes that my_reader
uses its command-line arguments to construct the potential completion list for the readline
library. You'd probably want the option to present a prompt as well.) (假设
my_reader
使用其命令行参数来构建readline
库的潜在完成列表。您可能还希望该选项显示提示。)
The readline documentation includes an example of an application which does simple custom completion; readline 文档包含一个应用程序示例,该示例执行简单的自定义完成; once you translate it from the K&R function prototype syntax, it might be pretty easy to adapt to your needs.
一旦您从 K&R 函数原型语法翻译它,它可能很容易适应您的需求。
Edit: After I looked at that example again, I thought it had a lot of unnecessary details, so I wrote the following example with fewer unnecessary details.编辑:在我再次查看那个例子后,我认为它有很多不必要的细节,所以我写了下面的例子,减少了不必要的细节。 I might upload it to github, but for now it's here even though it's nearly 100 lines:
我可能会把它上传到 github,但现在它就在这里,尽管它有将近 100 行:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <readline/readline.h>
static void version(const char* progname) {
fprintf(stderr, "%s 0.1\n", progname);
}
static void usage(const char* progname) {
fprintf(stderr, "Usage: %s [-fhv] [-p PROMPT] [-n PROGNAME] [COMPLETION...]\n", progname);
fprintf(stderr,
"Reads one line using readline, and prints it to stdout.\n"
"Returns success if a line was read.\n"
" -p PROMPT Output PROMPT before requesting input.\n"
" -n PROGNAME Set application name to PROGNAME for readline config file\n"
" (Default: %s).\n"
" -f Use filename completion as well as specified completions.\n"
" -h Print this help text and exit.\n"
" -v Print version number and exit.\n"
" COMPLETION word to add to the list of possible completions.\n",
progname);
}
/* Readline really likes globals, so none of its hooks take a context parameter. */
static char** completions = NULL;
static char* generate_next_completion(const char* text, int state) {
static int index = 0;
if (state == 0) index = 0; /* reset index if we're starting */
size_t textlen = strlen(text);
while (completions[index++])
if (strncmp(completions[index - 1], text, textlen) == 0)
return strdup(completions[index - 1]);
return NULL;
}
/* We use this if we will fall back to filename completion */
static char** generate_completions(const char* text, int start, int end) {
return rl_completion_matches(text, generate_next_completion);
}
int main (int argc, char **argv) {
const char* prompt = "";
const char* progname = strrchr(argv[0], '/');
progname = progname ? progname + 1 : argv[0];
rl_readline_name = progname;
bool use_file_completion = false;
for (;;) {
int opt = getopt(argc, argv, "+fp:n:hv");
switch (opt) {
case -1: break;
case 'f': use_file_completion = true; continue;
case 'p': prompt = optarg; continue;
case 'n': rl_readline_name = optarg; continue;
case 'h': usage(progname); return 0;
case 'v': version(progname); return 0;
default: usage(progname); return 2;
}
break;
}
/* The default is stdout, which would interfere with capturing output. */
rl_outstream = stderr;
completions = argv + optind;
rl_completion_entry_function = rl_filename_completion_function;
if (*completions) {
if (use_file_completion)
rl_attempted_completion_function = generate_completions;
else
rl_completion_entry_function = generate_next_completion;
} else {
/* No specified strings */
if (!use_file_completion)
rl_inhibit_completion = true;
}
char* line = readline(prompt);
if (line) {
puts(line);
free(line);
return 0;
} else {
fputc('\n', rl_outstream);
return 1;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.