除 cd 之外的其他命令的 Bash 自动完成变量

除 cd 之外的其他命令的 Bash 自动完成变量

我有环境变量$SCRIPT。通常我可以使用自动完成

vim $SC<tab>

并按预期完成工作。在某些系统上它不起作用。我已加载 bash 补全,但在这些情况下补全仅适用于目录(cd $SCRIPT_DIREC<tab>正在工作)。

主要是我对 bash 4.2 有问题。

什么shopt可以启用它?

编辑:我将我的shopt设置与正常工作的 Bash 4.3 和不工作的 Bash 4.2(Ubuntu 服务器 12.04)进行了比较,几乎没有什么区别(大多数是 4.3 特定的),将其余设置相同,但没有任何效果。

答案1

问题具体在于 shell 内置命令可以<tab>扩展参数名称,但外部命令则不能。这就是为什么cd有效。 echoread和任何其他内置函数继续像 OP 期望的那样工作。

与商店无关。

它位于 bash_completion 库中(/etc/bash_completion在我的 12.04.4 系统中)。版本 1.3 中存在导致此故障情况的某些内容。在 1.99 或之前的某个地方它被修复了。

如果bash_completion 库存~/.bash_completion在,它会自动获取源代码,因此我建议创建一个包含以下内容的文件,以重载 12.04.4 库中不适合您的函数定义。我做了一些小测试,但我使用的任何机器都没有受到这种情况的影响,所以买者自负。

# following functions are direct copy/pastes from
# bash_completion RELEASE: 1.99
# overloading `_longopt()' and adding `_variables()' and `_init_completion()'
# appears to be all that is needed to correct this.
# 
# http://unix.stackexchange.com/questions/126300/bash-autocomplete-variables-for-other-commands-than-cd

_init_completion()
{
    local exclude= flag outx errx inx OPTIND=1
    while getopts "n:e:o:i:s" flag "$@"; do
        case $flag in
            n) exclude+=$OPTARG ;;
            e) errx=$OPTARG ;;
            o) outx=$OPTARG ;;
            i) inx=$OPTARG ;;
            s) split=false ; exclude+== ;;
        esac
    done
    # For some reason completion functions are not invoked at all by
    # bash (at least as of 4.1.7) after the command line contains an
    # ampersand so we don't get a chance to deal with redirections
    # containing them, but if we did, hopefully the below would also
    # do the right thing with them...
    COMPREPLY=()
    local redir="@(?([0-9])<|?([0-9&])>?(>)|>&)"
    _get_comp_words_by_ref -n "$exclude<>&" cur prev words cword
    # Complete variable names.
    _variables && return 1
    # Complete on files if current is a redirect possibly followed by a
    # filename, e.g. ">foo", or previous is a "bare" redirect, e.g. ">".
    if [[ $cur == $redir* || $prev == $redir ]]; then
        local xspec
        case $cur in
            2'>'*) xspec=$errx ;;
            *'>'*) xspec=$outx ;;
            *'<'*) xspec=$inx ;;
            *)
                case $prev in
                    2'>'*) xspec=$errx ;;
                    *'>'*) xspec=$outx ;;
                    *'<'*) xspec=$inx ;;
                esac
                ;;
        esac
        cur="${cur##$redir}"
        _filedir $xspec
        return 1  
    fi
    # Remove all redirections so completions don't have to deal with them.
    local i skip  
    for (( i=1; i < ${#words[@]}; )); do
        if [[ ${words[i]} == $redir* ]]; then
            # If "bare" redirect, remove also the next word (skip=2).
            [[ ${words[i]} == $redir ]] && skip=2 || skip=1
            words=( "${words[@]:0:i}" "${words[@]:i+skip}" )
            [[ $i -le $cword ]] && cword=$(( cword - skip ))
        else
            i=$(( ++i ))
        fi
    done
    [[ $cword -eq 0 ]] && return 1
    prev=${words[cword-1]}
    [[ ${split-} ]] && _split_longopt && split=true
    return 0
}

_variables()
{
    if [[ $cur =~ ^(\$\{?)([A-Za-z0-9_]*)$ ]]; then
        [[ $cur == *{* ]] && local suffix=} || local suffix=
        COMPREPLY+=( $( compgen -P ${BASH_REMATCH[1]} -S "$suffix" -v -- \
            "${BASH_REMATCH[2]}" ) )
        return 0  
    fi
    return 1
}

_longopt()
{   
    local cur prev words cword split
    _init_completion -s || return
    case "${prev,,}" in
        --help|--usage|--version)
            return 0
            ;;
        --*dir*)
            _filedir -d
            return 0
            ;;
        --*file*|--*path*)
            _filedir
            return 0
            ;;
        --+([-a-z0-9_]))
            local argtype=$( $1 --help 2>&1 | sed -ne \
                "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p" )
            case ${argtype,,} in
                *dir*)
                    _filedir -d
                    return 0
                    ;;
                *file*|*path*)
                    _filedir
                    return 0
                    ;;
            esac
            ;;
    esac
    $split && return 0
    if [[ "$cur" == -* ]]; then
        COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | \
            sed -ne 's/.*\(--[-A-Za-z0-9]\{1,\}=\{0,1\}\).*/\1/p' | sort -u )" \
            -- "$cur" ) )
        [[ $COMPREPLY == *= ]] && compopt -o nospace
    elif [[ "$1" == @(mk|rm)dir ]]; then
        _filedir -d
    else
        _filedir
    fi
}

答案2

一种方法是,您只需将其别名设置为

alias Script="<Your $Script Here>"

在您的用户的 .profile 中/home/user/.profile,然后使用以下命令获取它:

$ . profile

然后检查它是否自动完成。

相关内容