您可能已经注意到,当多个选项与您的命令匹配时,bash显示如下:
foobar@myserv:~$ admin- admin-addrsync admin-adduser admin-delrsync admin-deluser admin-listsvn admin-addsvn admin-chmod admin-delsvn admin-listrsync
我正在寻找一种解决方案来显示新行上的每个可能的解决方案,类似于ls -l上的最后一列。更好的是,如果我可以应用这样的规则,这将是完美的:“如果您发现少于10个建议,请逐行显示,如果更多=>实际显示”。
Bash 4.2允许在全球范围内切换到1行建议的输出,如Grisha Levit’s helpful answer所述,它也链接到clever workaround以实现每个完成功能的解决方案。
以下是自定义完成的棘手解决方法。
解决这个问题一般来说,对于所有定义的完成,将会更加困难(如果有一种方法可以直接调用readline函数,可能会更容易,但是我没有找到一种方法)。
测试下面的概念证明:
>保存到文件并将其(。文件)导入到交互式shell中 – 这将:
>定义一个名为foo的命令(一个shell函数)
>其参数根据当前目录中的匹配文件名完成。
>(当foo实际被调用时,它只是以诊断形式打印其参数。)
>调用为:
foo [fileNamePrefix],然后按Tab键:
>如果当前目录中的2到9个文件匹配,您将看到所需的逐行显示。
>否则(1场比赛或10场以上比赛),将会正常完成。
限制:
>仅在正在编辑的命令行上应用于LAST参数时,完成才能正常工作。
>当完成实际插入到命令行中(一旦匹配是明确的),则不附加空格(解决方法需要此行为)。
>打印自定义格式化输出后第一次重新提示提示可能无法正常工作:重新绘制包含提示的命令行必须被模拟,因为没有直接方法来获取存储在$ PS1中的提示定义字符串的扩展版本,使用了一个解决方案(灵感来自http://stackoverflow.com/a/24006864/45375),它应该在典型的情况下工作,但不是万无一失的。
做法:
>定义和分配一个自定义完成shell函数到感兴趣的命令。
>自定义函数确定匹配,如果它们的计数在所需的范围内,则绕过正常完成机制并创建自定义格式的输出。
>自定义格式的输出(每行匹配在自己的行上)直接发送到终端> / dev / tty,然后提示符和命令行手动“重绘”以模拟标准完成行为。
>请参阅源代码中的注释以了解实现细节。
# Define the command (function) for which to establish custom command completion. # The command simply prints out all its arguments in diagnostic form. foo() { local a i=0; for a; do echo "\$$((i+=1))=[$a]"; done; } # Define the completion function that will generate the set of completions # when <tab> is pressed. # CAVEAT: # Only works properly if <tab> is pressed at the END of the command line,# i.e.,if completion is applied to the LAST argument. _complete_foo() { local currToken="${COMP_WORDS[COMP_CWORD]}" matches matchCount # Collect matches,providing the current command-line token as input. IFS=$'\n' read -d '' -ra matches <<<"$(compgen -A file "$currToken")" # Count matches. matchCount=${#matches[@]} # Output in custom format,depending on the number of matches. if (( matchCount > 1 && matchCount < 10 )); then # Output matches in CUSTOM format: # print the matches line by line,directly to the terminal. printf '\n%s' "${matches[@]}" >/dev/tty # !! We actually *must* pass out the current token as the result,# !! as it will otherwise be *removed* from the redrawn line,# !! even though $COMP_LINE *includes* that token. # !! Also,by passing out a nonempty result,we avoid the bell # !! signal that normally indicates a Failed completion. # !! However,by passing out a single result,a *space* will # !! be appended to the last token - unless the compspec # !! (mapping established via `complete`) was defined with # !! `-o nospace`. COMPREPLY=( "$currToken" ) # Finally,simulate redrawing the command line. # Obtain an *expanded version* of `$PS1` using a trick # inspired by http://stackoverflow.com/a/24006864/45375. # !! This is NOT foolproof,but hopefully works in most cases. expandedPrompt=$(PS1="$PS1" debian_chroot="$debian_chroot" "$BASH" --norc -i </dev/null 2>&1 | sed -n '${s/^\(.*\)exit$/\1/p;}') printf '\n%s%s' "$expandedPrompt" "$COMP_LINE" >/dev/tty else # Just 1 match or 10 or more matches? # Perform NORMAL completion: let bash handle it by # reporting matches via array variable `$COMPREPLY`. COMPREPLY=( "${matches[@]}" ) fi } # Map the completion function (`_complete_foo`) to the command (`foo`). # `-o nospace` ensures that no space is appended after a completion,# which is needed for our workaround. complete -o nospace -F _complete_foo -- foo