7

我正在为点文件管理实用程序编写 bash 完成脚本。该工具有一个命令dots diff [filename],将显示已安装的点文件和源点文件之间的差异。它还有一个命令dots files列出所有托管点文件的路径(相对于源目录)。我想用 files 命令的输出来完成 diff 命令。

这是文件输出的示例

X11/xkb/symbols/evan-custom
X11/xorg.conf.d/10-dual-monitors.conf
X11/xorg.conf.d/10-keylayout.conf
bash/aliases
bash/bashrc
bash/completion.d/dots
bash/profiles/standard-user
bash/profiles/systemd-user
bspwm/bspwmrc
compton/compton.conf
fontconfig/fonts.conf
git/config
git/ignore
gtk-2.0/gtkrc
gtk-3.0/settings.ini
mysql/config
mysql/grcat
ncmpcpp/config
pulse/client.conf
pulse/daemon.conf
pulse/default.pa
ssh/config
sublime-text-3/Packages/User/Preferences.sublime-settings
sxhkd/sxhkdrc
termite/config
transmission-daemon/settings.json
vim/vimrc

使用这样的东西

COMPREPLY=( $(compgen -W "$(dots files)" -- $cur) )

有效,但是当 readline 列出可用选项时,它会列出完整路径(上面的列表)。

我希望它把这些词当作文件路径来对待,并且在列出建议时只列出第一个正斜杠。

例如,如果我键入dots diff [tab][tab]以下内容,则应打印

X11/
bash/
bspwm/
compton/
fontconfig/
git/
gtk-2.0/
gtk-3.0/
mysql/
ncmpcpp/
pulse/
ssh/
sublime-text-3/
sxhkd/
termite/
transmission-daemon/
vim/

例如,如果我然后输入dots diff bash/[tab][tab]然后它会显示

aliases
bashrc
completion.d/
profiles/

理想情况下,我希望它实际上将其视为一条路径,以便将 readline 选项更改mark-directories为 off 将排除尾部斜杠。

我尝试过设置compopt -o filenames,但这反而给出了文件名的建议,而不是最初的路径。

这是我到目前为止的完成脚本

4

3 回答 3

6

我已经解决了这个问题。

诀窍是使用compopt -o filename然后切掉正在完成的路径部分,该部分是正在完成的目录的子目录。

这是代码

# Do completion from a passed list of paths
#
# Accepts 2 arguments
# 1. The list of paths to complete from
# 2. The current word being completed
__dots_path_comp()
{
    # This forces readline to only display the last item separated by a slash
    compopt -o filenames

    local IFS=$'\n'
    local k="${#COMPREPLY[@]}"

    for path in $(compgen -W "$1" -- $2)
    do
        local trailing_trim

        # Determine what to trim from the end
        trailing_trim="${path#${2%/*}/}/"
        trailing_trim="${trailing_trim#*/}"
        trailing_trim="${trailing_trim%/}"

        # Don't add a space if there is more to complete
        [[ "$trailing_trim" != "" ]] && compopt -o nospace

        # Remove the slash if mark-directories is off
        if ! _rl_enabled mark-directories
        then
            # If The current typed path doesnt have a slash in it yet check if
            # it is the full first portion of a path and ignore everything after
            # if it is. We don't have to do this once the typed path has a slash
            # in it as the logic above will pick up on it
            [[ "$2" != */* && "$path" == ${2}/* ]] && path="$2/$trailing_trim"    

            trailing_trim="/$trailing_trim"
        fi

        COMPREPLY[k++]="${path%%${trailing_trim}}"
    done
}
于 2013-11-14T09:36:52.700 回答
0

一个肮脏的黑客将取代$(dots files)

$(dots files | sed 's,/.*/,,' | uniq)

这样完整的东西就会读作

COMPREPLY=( $(compgen -W "$(dots files | sed 's,/.*/,,' | uniq)" -- "$cur") )

现在我个人不太喜欢这种方法。相反,您能否修改您的脚本以接受例如basedirs将输出所需内容的语句?

此外,您应该注意,如果名称中有空格或其他有趣的符号,则完成将无法按预期工作。

于 2013-11-03T12:24:45.663 回答
0

我认为您可以通过完整的 -F 参数获得所需的内容。从文档

首先调用使用 -F 指定的任何函数。该函数可以使用任何 shell 工具,包括下面描述的 compgen 和 compopt 内置函数(请参阅可编程完成内置函数)来生成匹配项。它必须将可能的完成放在 COMPREPLY 数组变量中。

所以,像这样设置它:

complete -F _dots_complete dots

然后你的功能:

function _dots_complete {
    # your filtering logic to calculate the results array
    COMPREPLY=( "${results[@]}" )
}

在您的函数中 $1 是当前单词的位置, $2 .. $n 是单词本身。

于 2013-11-03T12:30:17.730 回答