5

我正在尝试编写一个 bash 完成功能,它可以让我完成其他 shell 所在的目录名称。

例如,假设我在 中打开了另一个 shell /very/long/path/name,并且我当前位于包含子目录foobar. 当我输入时cd <Tab>,我想看到:

$ cd <Tab>
foo/  bar/  /very/long/path/name

我有这个命令来生成潜在完成列表:

ps -Cbash -opid= | xargs pwdx | cut -d" " -f2 | sort -u | while read; do echo ${REPLY#$PWD/}; done | grep -v "^$"

为简洁起见,我将其写为...pipeline....

在我的系统上,有一个_cd生成常规完成的函数:

$ complete -p cd
complete -o nospace -F _cd cd

我想重用这个_cd函数,因为它很重要(大约 30 行代码,根据type _cd)。如果解决方案重用已定义的任何完成,无论它是否基于名为_cd.

我认为该-C选项complete听起来很有希望,但我无法让它发挥作用:

$ complete -C '...pipeline...' cd
$ cd <Tab>grep: cd: No such file or directory
grep: : No such file or directory
grep: cd: No such file or directory

编写我自己的包装函数 for -F,它附加到COMPREPLY数组中,也不太奏效:

$ function _cd2() { _cd; COMPREPLY=( ${COMPREPLY[@]} $(...pipeline...) ); }
$ cd <Tab>
foo/  bar/  name/

它剥离了除最后一个之外的所有路径组件。我认为它一定是这样_cd做的,但我不知道如何抵消它。

如果我_cd从 中删除调用_cd2,我会看到完成,但它们没有正确完成部分目录名称。如果我键入cd /ve<Tab>,它仍然显示完整路径,而实际上并没有完成我的命令行。

我怎样才能让它做我想做的事?


附录: 的完整定义_cd

$ type _cd
_cd is a function
_cd () 
{ 
    local cur prev words cword;
    _init_completion || return;
    local IFS='
' i j k;
    compopt -o filenames;
    if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then
        _filedir -d;
        return 0;
    fi;
    local -r mark_dirs=$(_rl_enabled mark-directories && echo y);
    local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y);
    for i in ${CDPATH//:/'
'};
    do
        k="${#COMPREPLY[@]}";
        for j in $( compgen -d $i/$cur );
        do
            if [[ ( -n $mark_symdirs && -h $j || -n $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then
                j+="/";
            fi;
            COMPREPLY[k++]=${j#$i/};
        done;
    done;
    _filedir -d;
    if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
        i=${COMPREPLY[0]};
        if [[ "$i" == "$cur" && $i != "*/" ]]; then
            COMPREPLY[0]="${i}/";
        fi;
    fi;
    return 0
}
4

1 回答 1

1

您需要根据组合选项列表评估当前匹配。这是一个说明移动部件的测试脚本:

#!/bin/bash

mkdir -p {my,other}/path/to/{a,b,c}

function _cd() {
    COMPREPLY=( my/path/to/a my/path/to/b );
}
complete -o nospace -F _cd cd

function _cd2() {
    local cur opts;
    cur="${COMP_WORDS[COMP_CWORD]}";
    _cd;
    opts="${COMPREPLY[@]} other/path/to/c";        # here we combine options
    COMPREPLY=($(compgen -W "${opts}" -- ${cur})); # here is the secret sauce
}
complete -F _cd2 cd

complete -p cd

最重要的点在 : 的compgen部分中,它从组合的选项集中(在)_cd2中选择最合适的选项。$opts

于 2014-10-24T15:05:17.333 回答