是否可以对 bash 自动完成输出进行分类?
Is it possible to categorize the bash autocomplete output?
我已阅读bash programmable completion on the GNU website, and many good Q&A on Whosebug and unix.stackexchange.com。幸运的是,我制作了一个自动完成脚本,它几乎完全符合我的要求。但是,我需要对建议进行格式化。
有没有办法根据建议的类型对建议进行分类。例如,我想要这样的东西:
$ foo --bar # press TAB twice and get the result as below:
* Directories:
foo/ bar/ baz/
* Files:
foo.sh bar.sh baz.sh
* Options:
--foo= --bar --baz
而不是这个:
$ foo --bar # press TAB twice and get the result as below:
--bar --baz --foo= bar/ baz/
foo/ bar.sh baz.sh foo.sh
完成脚本的最小代码片段为:
_foo(){
COMPREPLY=()
local word=""
local prev=""
# Suggest directories
COMPREPLY+=( $( compgen -d -- $word ) )
# Suggest '*.sh' files with negating the '-X' filter pattern
COMPREPLY+=( $( compgen -f -X "![.][sS][hH]$" -- $word ) )
# Suggest options in the '-W' word list
COMPREPLY+=( $( compgen -W "--foo= --bar --baz" -- $word ) )
}
complete -F _foo foo
感谢您的时间、想法和见解:)
更新:
我调整了@Socowi 的 ,这样额外的 \
就不会出现在 =
标志之前。
compgenSection() {
title="* :"
shift
local entries
mapfile -t entries < <(compgen "$@")
(( "${#entries[@]}" == 0 )) && return
COMPREPLY_SECTIONLESS+=("${entries[@]}")
mapfile -tO "${#COMPREPLY[@]}" COMPREPLY < <(
printf "%$((-COLUMNS/2))s\n" "$title"
printf %s\n "${entries[@]}" | sort | column -s $'\n' | expand
)
[[ "${COMPREPLY[@]}" =~ "=" ]] && compopt -o nospace
}
_foo(){
local word=""
COMPREPLY_SECTIONLESS=()
compgenSection Directories -d -S / -- "$word"
compgenSection Files -f -X '!*.[sS][hH]' -- "$word"
compgenSection Options -W '--foo= --bar --baz' -- "$word"
(( "${#COMPREPLY_SECTIONLESS[@]}" <= 1 )) &&
COMPREPLY=("${COMPREPLY_SECTIONLESS[@]}")
}
complete -o nosort -F _foo foo
我不能确定自定义格式是否不可能,但如果是的话,这里有一个可能的解决方法以防万一:
将章节标题添加到 COMPREPLY
以完全显示它们。现在我们还有其他问题需要解决:
- 完成排序。章节标题出现在错误的位置。
解决方法: 使用 complete -o nosort ...
以正确的顺序显示章节标题和条目。您可以再次手动对每个部分中的条目进行排序。
- 由于 bash 在多列中显示完成,因此部分标题可能与其他条目出现在同一行中。
解决方法: 强制每个完成条目显示在其自己的行中。 Bash 将 COMPREPLY
中的条目格式化为大小均匀的列。如果其中一列的宽度超过终端的一半,则所有条目将显示在各自的行中。只需用空格填充章节标题即可。
- 现在所有完成也都显示在单行中,但我们希望它们显示在列中。
解决方法: 手动将所有完成项放入列中。然后将每一行(可能有多个条目)添加为 COMPGEN
. 中的单个条目
- 按一次 tab 自动完成不起作用。由于章节标题,总是存在不止一种可能性。
解决方法: 确保在 word=
更改时相应地调整 COMPREPLY
。如果在所有部分中只有一个匹配项,则必须取消 headers 部分,以便 COMPREPLY
仅包含该单个匹配项。在这里我们还抑制了空白部分,使它看起来更漂亮。
实施
compgenSection() {
title="* :"
shift
local entries
mapfile -t entries < <(compgen "$@")
(( "${#entries[@]}" == 0 )) && return
COMPREPLY_SECTIONLESS+=("${entries[@]}")
mapfile -tO "${#COMPREPLY[@]}" COMPREPLY < <(
printf "%$((-COLUMNS/2))s\n" "$title"
printf %s\n "${entries[@]}" | sort | column -s $'\n' | expand
)
}
_foo(){
local word=""
COMPREPLY_SECTIONLESS=()
compgenSection Directories -d -- "$word"
compgenSection Files -f -X '!*.[sS][hH]' -- "$word"
compgenSection Options -W '--foo= --bar --baz' -- "$word"
(( "${#COMPREPLY_SECTIONLESS[@]}" <= 1 )) &&
COMPREPLY=("${COMPREPLY_SECTIONLESS[@]}")
}
complete -o nosort -o filenames -F _foo foo
请注意,我必须更改您的 compgen -f -X "![.][sS][hH]$"
,因为 compgen
接受的模式是 glob 而不是正则表达式。
我还解决了 array=( $(compgen ...) )
的问题,其中带空格的文件名被拆分为多个完成条目。对于其中包含换行符的文件名,这仍然会发生。然而,这似乎是正常行为:尝试 touch $'b\na'; cat b
tabtab,它打印 a b
而不是 $'b\na'
.
我还添加了选项 complete -o filenames
,以便在 auto-completion 上正确引用其中包含空格的条目。如果这干扰了选项的完成(您可能需要实际空间),您可以 运行 通过 printf %q
手动输入文件和目录条目以仍然获得正确的引用。
测试
以下是互动 session 中的一些示例。
⌨️
是提示符。 ↹
标记我按下 tab 的位置。
⌨️ mkdir /tmp/test/{,foo,bar,baz}; cd /tmp/test; touch foo.sh bar.sh baz.sh
⌨️ foo ↹↹
* Directories:
bar baz foo
* Files:
bar.sh baz.sh foo.sh
* Options:
--bar --baz --foo=
⌨️ foo f↹↹
* Directories:
foo
* Files:
foo.sh
⌨️ foo foo.↹ # auto-completes to `foo foo.sh`
我已阅读bash programmable completion on the GNU website, and many good Q&A on Whosebug and unix.stackexchange.com。幸运的是,我制作了一个自动完成脚本,它几乎完全符合我的要求。但是,我需要对建议进行格式化。
有没有办法根据建议的类型对建议进行分类。例如,我想要这样的东西:
$ foo --bar # press TAB twice and get the result as below:
* Directories:
foo/ bar/ baz/
* Files:
foo.sh bar.sh baz.sh
* Options:
--foo= --bar --baz
而不是这个:
$ foo --bar # press TAB twice and get the result as below:
--bar --baz --foo= bar/ baz/
foo/ bar.sh baz.sh foo.sh
完成脚本的最小代码片段为:
_foo(){
COMPREPLY=()
local word=""
local prev=""
# Suggest directories
COMPREPLY+=( $( compgen -d -- $word ) )
# Suggest '*.sh' files with negating the '-X' filter pattern
COMPREPLY+=( $( compgen -f -X "![.][sS][hH]$" -- $word ) )
# Suggest options in the '-W' word list
COMPREPLY+=( $( compgen -W "--foo= --bar --baz" -- $word ) )
}
complete -F _foo foo
感谢您的时间、想法和见解:)
更新:
我调整了@Socowi 的 \
就不会出现在 =
标志之前。
compgenSection() {
title="* :"
shift
local entries
mapfile -t entries < <(compgen "$@")
(( "${#entries[@]}" == 0 )) && return
COMPREPLY_SECTIONLESS+=("${entries[@]}")
mapfile -tO "${#COMPREPLY[@]}" COMPREPLY < <(
printf "%$((-COLUMNS/2))s\n" "$title"
printf %s\n "${entries[@]}" | sort | column -s $'\n' | expand
)
[[ "${COMPREPLY[@]}" =~ "=" ]] && compopt -o nospace
}
_foo(){
local word=""
COMPREPLY_SECTIONLESS=()
compgenSection Directories -d -S / -- "$word"
compgenSection Files -f -X '!*.[sS][hH]' -- "$word"
compgenSection Options -W '--foo= --bar --baz' -- "$word"
(( "${#COMPREPLY_SECTIONLESS[@]}" <= 1 )) &&
COMPREPLY=("${COMPREPLY_SECTIONLESS[@]}")
}
complete -o nosort -F _foo foo
我不能确定自定义格式是否不可能,但如果是的话,这里有一个可能的解决方法以防万一:
将章节标题添加到 COMPREPLY
以完全显示它们。现在我们还有其他问题需要解决:
- 完成排序。章节标题出现在错误的位置。
解决方法: 使用complete -o nosort ...
以正确的顺序显示章节标题和条目。您可以再次手动对每个部分中的条目进行排序。 - 由于 bash 在多列中显示完成,因此部分标题可能与其他条目出现在同一行中。
解决方法: 强制每个完成条目显示在其自己的行中。 Bash 将COMPREPLY
中的条目格式化为大小均匀的列。如果其中一列的宽度超过终端的一半,则所有条目将显示在各自的行中。只需用空格填充章节标题即可。 - 现在所有完成也都显示在单行中,但我们希望它们显示在列中。
解决方法: 手动将所有完成项放入列中。然后将每一行(可能有多个条目)添加为COMPGEN
. 中的单个条目
- 按一次 tab 自动完成不起作用。由于章节标题,总是存在不止一种可能性。
解决方法: 确保在word=
更改时相应地调整COMPREPLY
。如果在所有部分中只有一个匹配项,则必须取消 headers 部分,以便COMPREPLY
仅包含该单个匹配项。在这里我们还抑制了空白部分,使它看起来更漂亮。
实施
compgenSection() {
title="* :"
shift
local entries
mapfile -t entries < <(compgen "$@")
(( "${#entries[@]}" == 0 )) && return
COMPREPLY_SECTIONLESS+=("${entries[@]}")
mapfile -tO "${#COMPREPLY[@]}" COMPREPLY < <(
printf "%$((-COLUMNS/2))s\n" "$title"
printf %s\n "${entries[@]}" | sort | column -s $'\n' | expand
)
}
_foo(){
local word=""
COMPREPLY_SECTIONLESS=()
compgenSection Directories -d -- "$word"
compgenSection Files -f -X '!*.[sS][hH]' -- "$word"
compgenSection Options -W '--foo= --bar --baz' -- "$word"
(( "${#COMPREPLY_SECTIONLESS[@]}" <= 1 )) &&
COMPREPLY=("${COMPREPLY_SECTIONLESS[@]}")
}
complete -o nosort -o filenames -F _foo foo
请注意,我必须更改您的 compgen -f -X "![.][sS][hH]$"
,因为 compgen
接受的模式是 glob 而不是正则表达式。
我还解决了 array=( $(compgen ...) )
的问题,其中带空格的文件名被拆分为多个完成条目。对于其中包含换行符的文件名,这仍然会发生。然而,这似乎是正常行为:尝试 touch $'b\na'; cat b
tabtab,它打印 a b
而不是 $'b\na'
.
我还添加了选项 complete -o filenames
,以便在 auto-completion 上正确引用其中包含空格的条目。如果这干扰了选项的完成(您可能需要实际空间),您可以 运行 通过 printf %q
手动输入文件和目录条目以仍然获得正确的引用。
测试
以下是互动 session 中的一些示例。
⌨️
是提示符。 ↹
标记我按下 tab 的位置。
⌨️ mkdir /tmp/test/{,foo,bar,baz}; cd /tmp/test; touch foo.sh bar.sh baz.sh
⌨️ foo ↹↹
* Directories:
bar baz foo
* Files:
bar.sh baz.sh foo.sh
* Options:
--bar --baz --foo=
⌨️ foo f↹↹
* Directories:
foo
* Files:
foo.sh
⌨️ foo foo.↹ # auto-completes to `foo foo.sh`