bash 脚本可以包含自己的自动完成功能吗?

bash 脚本可以包含自己的自动完成功能吗?

有很多可用资源(1,2,3)解释了如何利用 bash 自动完成命令和参数的能力,但所有这些资源都需要向用户添加代码,~/.bash_profile或者/etc/bash_completion.d/*有没有办法使脚本及其可用的完成自包含?作为一种粗制和不完整的例子:

〜/ bin / script-with-integrated-autocomplete.sh

#!/usr/bin/env bash

function _completions {
  complete ...
}

if [ "$1" == "completions" ]; then
  _completions
  exit 0
fi

# (Whatever the script really does goes here.)
# ...
# ...

破坏交易的因素(在这个问题的上下文中)是上面的例子仍然需要你添加类似的东西~/bin/script-with-integrated-autocomplete.sh completions.profile完成。

有没有办法让单个 bash 脚本(文件)声明自己的完成并让 bash 识别它们在调用时(最好没有额外的系统或环境设置)?

答案1

正如对您的问题的评论中所述,如果没有任何先前的配置并且不更改当前环境,Bash 无法完成命令的命令行。 Bash 不会查看文件,也不会尝试解释它们,除非您显式执行或获取它们。

我对此主题的理解是,由于兼容性和资源问题,这样的功能不太可能很快进入 Bash 中——作为一个 shell,它应该能够可靠地运行在各种不同的系统上,具有多种用途并适应各种用户的喜好;并且只有一个人作为开发者和维护者1

虽然 Bash 提供了实现自动完成的工具,但完成生成器似乎在设计上就被设计为外部设施。事实上,您所寻找的内容可以通过现有工具和少量工作轻松实现。

给定示例脚本foo

#!/bin/bash

# Setting up completion for foo. Only if the script is sourced
if [ "$0" != "$BASH_SOURCE" ]; then
    _foo() {
        local cur
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        COMPREPLY=( $(compgen -W 'bar baz' -- "$cur") )
        return 0
    }
    complete -F "_foo" "foo"
    return
fi

# The body of foo -- what it does when executed
echo foo "$*"
# ...

使用bash 完成foo,可以通过将符号链接添加到存储动态加载的用户完成的目录来启用自包含自动完成:

ln -s /path/to/foo ~/.local/share/bash-completion/completions/

确切的路径可能会有所不同,您可以参考 bash-completion 的常见问题解答以了解如何在您的系统上检查其值。

如果没有 bash-completion,一种方法可能是定义一个默认的完成函数,如果命令满足某些条件,则该函数将提供命令(对应的文件);在这里,我们使用关联数组作为白名单(以避免盲目获取系统上的每个命令)。必须将此代码添加到您的.bashrc(或等效文件,即每次启动交互式 shell 时在当前环境中获取的文件):

_default_completion () {
    # Do nothing when completion is invoked for an empty command
    if [ "$1" = "" ]; then
        return 1
    fi
    # Get the command path, exit with failure if it can't be found
    cmd=$(type -p "$1") || return 1
    if [ "${_compwhitelist["${cmd##*/}"]}" = y ]; then
        # Source the file corresponding to the command; on success,
        # return 124 to trigger a new autocompletion attempt
        source "$cmd" && return 124
    fi
}
complete -D -F _default_completion -o bashdefault -o default

# The list of commands with embedded completion
declare -A _compwhitelist
_compwhitelist[foo]=y

我知道,您要求一个不需要编辑配置文件的解决方案,但请注意,一定量的配置是总是必需的。其中很大一部分是由发行版/软件包维护者完成的,即使用户可能永远不会意识到这一点。

1 参考资料:巴什主页在 Chet Ramey 的网站上并在相关 Bash 中点“A1”常问问题

答案2

是的,它可以。您需要做的就是检查环境变量 COMP_LINE 是否已设置,然后简单地回显所有应该可用的自动完成选项。每个选项一行,然后退出脚本。该脚本甚至不需要是 shell 脚本。可以是任何东西,Python、PHP、Golang 等等。

伪代码:

if COMP_LINE {
  echo "Foo"
  echo "Bar"
  Exit 0
}

//Rest of the script here

然后只需将脚本添加到 shell 自动完成规则中即可:

完成 -C /usr/bin/the_script.sh the_script.sh

现在,输入“the_script.sh <tab> <tab>”后,它将显示“foo,bar”选项。

答案3

打开 shell 并输入,asdfasdf Tab您将看到它会自动完成$CWD.

如果您可以找到为此调用的“默认完成”并为其制作包装器,则一次修改将适用于所有脚本。

包装器可以检查嵌入式完成,然后回退到原始完成。所有脚本都必须遵循您提出的任何标准。我认为响应COMP_LINECOMP_WORDSenv vars 将是一个不错的选择。

看来这应该已经有人完成了。我从不认为我的想法是开创性的。

相关内容