使用 sudo 时 bash 不会自动完成我的命令?

使用 sudo 时 bash 不会自动完成我的命令?

我正在使用 SaltStack。我想在调用salt命令时自动完成小兵名称。

以下行已添加到~/.bashrc

complete -o default -o nospace -W "$(sudo ls -1 /var/cache/salt/master/minions)" salt

然后输入→ ;我可以看到它按预期工作:salt inTabsalt integration-Tab

$ 盐集成-TabTab
集成-c 集成-u 集成-u2

为了与 一起使用sudo,我添加了complete -cf sudointo ~/.bashrc,但它不起作用:

    须藤盐Tab

什么也没返回。

我还尝试安装bash_completion并将以下行添加到~/.bash_profile

if [ -f $(brew --prefix)/etc/bash_completion ]; then
    . $(brew --prefix)/etc/bash_completion
fi

但没有运气。

我错过了什么?


更新

哦,我首先想说的是有时它是有效的:

$ sudo salt 集成TabTab
-集成-c 集成-u 集成-u2

有时却并非如此。

首先,让我们看看 bash_completion 包做了多少事情。

我该如何检查?这是我的功能:

# a wrapper method for the next one, when the offset is unknown
_command()
{
    local offset i

    # find actual offset, as position of the first non-option
    offset=1
    for (( i=1; i <= COMP_CWORD; i++ )); do
        if [[ "${COMP_WORDS[i]}" != -* ]]; then
            offset=$i
            break
        fi
    done
    _command_offset $offset
}

# A meta-command completion function for commands like sudo(8), which need to
# first complete on a command, then complete according to that command's own
# completion definition - currently not quite foolproof (e.g. mount and umount
# don't work properly), but still quite useful.
#
_command_offset()
{
    local cur func cline cspec noglob cmd i char_offset word_offset \
        _COMMAND_FUNC _COMMAND_FUNC_ARGS

    word_offset=$1

    # rewrite current completion context before invoking
    # actual command completion

    # find new first word position, then
    # rewrite COMP_LINE and adjust COMP_POINT
    local first_word=${COMP_WORDS[$word_offset]}
    for (( i=0; i <= ${#COMP_LINE}; i++ )); do
        if [[ "${COMP_LINE:$i:${#first_word}}" == "$first_word" ]]; then
            char_offset=$i
            break
        fi
    done
    COMP_LINE=${COMP_LINE:$char_offset}
    COMP_POINT=$(( COMP_POINT - $char_offset ))

    # shift COMP_WORDS elements and adjust COMP_CWORD
    for (( i=0; i <= COMP_CWORD - $word_offset; i++ )); do
        COMP_WORDS[i]=${COMP_WORDS[i+$word_offset]}
    done
    for (( i; i <= COMP_CWORD; i++ )); do
        unset COMP_WORDS[i];
    done
    COMP_CWORD=$(( $COMP_CWORD - $word_offset ))

    COMPREPLY=()
    _get_comp_words_by_ref cur

    if [[ $COMP_CWORD -eq 0 ]]; then
        _compopt_o_filenames
        COMPREPLY=( $( compgen -c -- "$cur" ) )
    else
        cmd=${COMP_WORDS[0]}
        if complete -p ${cmd##*/} &>/dev/null; then
            cspec=$( complete -p ${cmd##*/} )
            if [ "${cspec#* -F }" != "$cspec" ]; then
                # complete -F <function>

                # get function name
                func=${cspec#*-F }
                func=${func%% *}

                if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
                    $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" "${COMP_WORDS[${#COMP_WORDS[@]}-2]}"
                else
                    $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}"
                fi

                # remove any \: generated by a command that doesn't
                # default to filenames or dirnames (e.g. sudo chown)
                # FIXME: I'm pretty sure this does not work!
                if [ "${cspec#*-o }" != "$cspec" ]; then
                    cspec=${cspec#*-o }
                    cspec=${cspec%% *}
                    if [[ "$cspec" != @(dir|file)names ]]; then
                        COMPREPLY=("${COMPREPLY[@]//\\\\:/:}")
                    else
                        _compopt_o_filenames
                    fi
                fi
            elif [ -n "$cspec" ]; then
                cspec=${cspec#complete};
                cspec=${cspec%%${cmd##*/}};
                COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) );
            fi
        elif [ ${#COMPREPLY[@]} -eq 0 ]; then
            _filedir
        fi
    fi
}

如果您输入sudo mkdirTabTab,它会显示目录列表吗?

是的:

$ sudo mkdir TabTab
.FontForge/ .djangopypi2/ .ievms/ .ssh/ .wireshark-etc/

答案1

检查set -o是否posix启用了可能的模式。如果是这样,请禁用set +o posix

在“posix”模式下,由于某种原因,bash 的制表符补全在“vi 模式”下被禁用。我没有看到任何解释为什么会出现这种行为,也没有看到为什么它特定于 vi 模式,所以我没有费心解释。

PS:这个答案更适合通过搜索引擎偶然发现问题的人......而不适合您的特定设置。

更新
切特·雷米

在POSIX模式下,vi编辑模式不应该通过映射tab来完成。这是因为 POSIX.2 完全规定了 vi 编辑模式的行为,并且标准要求 tab 默认映射到 self insert。

答案2

快速回答:

  • 安装bash-completion
  • 来源bash_completionBash启动时
  • Bash在启动时添加您的 compspec
  • 不要sudo用以下内容覆盖 compspeccomplete -cf sudo

我想你用的MacOSXbrew.

尝试:

brew update
brew install bash-completion
brew info bash-completion
# bash-completion: stable 1.3
. $(brew --prefix)/etc/bash_completion
complete -p sudo

你应该看到这样的东西:

complete -F _root_command sudo

测试:

function _comp_foo() { COMPREPLY=($(compgen -W 'a b c' -- "$2")); }
complete -F _comp_foo foo

类型foo SpaceTabTab
您应该看到a b c

类型sudo foo SpaceTabTab
您应该看到a b c

complete -fc sudo然后从初始化文件中删除~/.bash_profile~/.bashrc等)
将以下行添加到初始化文件中:

if [ -f $(brew --prefix)/etc/bash_completion ]; then
  . $(brew --prefix)/etc/bash_completion
fi

# Your compspec here
complete -o default -o nospace -W "$(sudo ls -1 /var/cache/salt/master/minions)" salt

重新打开终端。
类型complete -p sudo
你应该看看complete -F _root_command sudo

类型complete -p salt
你应该看到这样的东西:

complete -o default -o nospace -W 'a
b
c' salt

bash-completion2.* 注意
您可以安装bash-completion2https://github.com/Homebrew/homebrew/issues/19258
但:

  • 2.*Bash 4.*
  • 2.*使用-Dcompspecs 延迟加载选项。因此,complete -p sudo输出complete: sudo: no completion specification直到您键入sudoSpaceTab

答案3

sudo除非您使用 请求,否则不会为您提供登录 shell -i。所以你会发现没有它你需要加载系统的补全:

. /etc/bash_completion

如果您使用 运行-i,则将$HOME~root,因此写入用户的任何内容~/.bashrc都不会被读取。如果您可以将完成的内容与其余部分分开~/.bashrc,请使用 和 来获取它.,这应该可以工作。

相关内容