bash 中的转义斜线完成

Escape slashes in bash complete

我尝试使用 bash complete 内置函数来显示命令的不同选项。

I have problems when an option contains a path like in -F/dev/null.

目前我正在使用

#!/bin/bash

_xyz-completion ()
{
  local cur

  COMPREPLY=()   # Array variable storing the possible completions.
  cur=${COMP_WORDS[COMP_CWORD]}

  case "$cur" in
    -*)
    COMPREPLY=( $( compgen -W "-oOption1 -F/dev/null" -- $cur ) )
    ;;
  esac

  return 0
}

complete -F _xyz-completion -o filenames xyz

如果已经键入了 -F,则 Tab 会成功完成它。

但如果只输入 -,则 Tab 显示

null       -oOption1

但我希望看到

-F/dev/null       -oOption1

我已经试过了 -F\/dev\/null-F//dev//null"-F/dev/null"-F\\/dev\\/null

这似乎只是一个显示问题,因为完成本身按预期工作。

我看不出如何适当转义 `-F/dev/null` 中的斜杠。


评论评论:

1)

Never mind, it's a problem also if -F is replaced by a non-option such as -Q. – Benjamin W.

这不是问题,-F 看起来像是 complete 本身的一个选项,因为如果我将其更改为 xOPTION1 xF/dev/null

它甚至会失败

2)

I'm wondering what compgen -W "-oOption1 -F/dev/null" -- - displays for you.

显示(如预期)

-oOption1
-F/dev/null

如前所述,-F 成功完成至 -F/dev/null

如果您从 complete 中删除 -o filenames 选项,您的示例将按预期工作。这是有道理的,因为补全不是文件名。这是 bash 版本 5.0.2(1)。

所以:

#!/bin/bash

_xyz-completion ()
{
  local cur

  COMPREPLY=()   # Array variable storing the possible completions.
  cur=${COMP_WORDS[COMP_CWORD]}

  case "$cur" in
    -*)
    COMPREPLY=( $( compgen -W "-oOption1 -F/dev/null" -- $cur ) )
    ;;
  esac

  return 0
}

complete -F _xyz-completion xyz

当有斜杠时,它会截断部分完成,这绝对是一个错误。并且只有在显示选项时,实际补全才正确。

编辑:

进一步研究后,filenames 选项用于转义可能包含空格或其他中断字符的字符串。基本上清理 shell 的文件名。来自 Programmable Completion Built-in man page

-o filenames:

Tell Readline that the compspec generates filenames, so it can perform any filename-specific processing (like adding a slash to directory names, quoting special characters, or suppressing trailing spaces). This option is intended to be used with shell functions specified with -F.

显然这包括去掉最后一个斜线之前的所有内容。

编辑 2:

这是来自 readline 源的评论,bash 用于文件名完成。我从 https://git.savannah.gnu.org/git/bash.git 的 bash 仓库中得到了这个。大师,所以在撰写本文时是 5.0 补丁 3。

./lib/readline/complete.c 第 697 行

/* Return the portion of PATHNAME that should be output when listing
   possible completions.  If we are hacking filename completion, we
   are only interested in the basename, the portion following the
   final slash.  Otherwise, we return what we were passed.  Since
   printing empty strings is not very informative, if we're doing
   filename completion, and the basename is the empty string, we look
   for the previous slash and return the portion following that.  If
   there's no previous slash, we just return what we were passed. */
static char *
printable_part (char *pathname)

对于文件名补全,它只想打印基本名称,以及最后一个斜线后的所有内容。