自定义bash完成输出:新行上的每个建议

4wk*_*wk_ 21 unix bash customization bash-completion

键入内容时,通常会使用bash自动完成:例如,您开始编写命令,然后键入TAB以获取其余部分.

您可能已经注意到,当多个选项与您的命令匹配时,bash会显示如下:

foobar@myserv:~$ admin-
admin-addrsync         admin-adduser          admin-delrsync         admin-deluser          admin-listsvn
admin-addsvn           admin-chmod            admin-delsvn           admin-listrsync
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种解决方案,在新线上显示每个可能的解决方案,类似于上一栏的最后一栏ls -l.更好的是,如果我可以应用这样的规则将是完美的:"如果您发现少于10条建议,则逐行显示,如果更多=>实际显示".

mkl*_*nt0 10

bash不幸的是,版本4.2之前不允许对完成输出格式进行任何控制.

Bash 4.2+允许在全球范围内切换到每线建议1个输出,如Grisha Levit的有用答案中所述,该答案还链接到实现每完成功能解决方案的巧妙解决方法.

下面是一个棘手的解决办法定制完成. 解决这个问题一般,对所有定义的落成,将是非常困难(如果有调用方式readline功能直接,它可能是更容易,但我还没有找到一种方法来做到这一点).

要测试以下概念证明:

  • 保存到文件和它(. file在你的交互shell) -这将:
    • 定义一个命名的命令foo(一个shell函数)
    • 其参数基于当前目录中的匹配文件名完成.
    • (foo实际调用时,它只是以诊断形式打印其参数.)
  • 调用: foo [fileNamePrefix],然后按tab:
    • 如果当前目录中的2到9个文件匹配,您将看到所需的逐行显示.
    • 否则(1场比赛或10场或更多场比赛),将进行正常完成.

限制:

  • 完成仅在应用于正在编辑的命令行上的LAST参数时才能正常工作.
  • 当实际在命令行中插入完成时(一旦匹配明确),就不会向其附加空格(解决方法需要此行为).
  • 在打印自定义格式的输出后第一次重新绘制提示可能无法正常工作:必须模拟重绘包括提示的命令行,并且因为没有直接的方法来获取存储在其中的提示定义字符串的扩展版本$PS1,所以解决方法(受/sf/answers/1680480511/启发)使用,它应该在典型的情况下工作,但不是万无一失的.

方法:

  • 定义并为感兴趣的命令分配自定义完成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 https://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
Run Code Online (Sandbox Code Playgroud)


Gri*_*vit 5

bash4.2+(更广泛地说,使用readline6.2+的应用程序)通过使用completion-display-width变量来支持此功能。

执行完成时用于显示可能的匹配项的屏幕列数。如果该值小于0或大于终端屏幕宽度,则将忽略该值。值为0将使匹配项每行显示一次。默认值为-1。

运行以下命令来设置当前会话的所有完成1的行为:

bind 'set completion-display-width 0'
Run Code Online (Sandbox Code Playgroud)

或将您的~/.inputrc2个文件修改为:

set completion-display-width 0
Run Code Online (Sandbox Code Playgroud)

更改所有新Shell的行为。

1请参见此处,了解用于控制各个自定义完成功能的此行为的方法。

2对的readline init文件搜索路径$INPUTRC~/.inputrc/etc/inputrc所以修改文件适合您。