如何使用自动完成功能创建脚本?


119

当我使用like之svn类的程序并输入Gnome Terminal时:

svn upd

并点击Tab它自动完成以:

svn update

是否可以在我的自定义bash脚本中执行类似的操作?


解释“ bash脚本”,是指在编辑脚本时?您想怎么办?
布鲁诺·佩雷拉

3
在控制台中使用脚本时
UAdapter

至于您要填写的内容的地方,请参阅此问题以及那里已被接受的答案的注释。
佳诺

Answers:


44

您可以使用Programmable Completion。有看/etc/bash_completion/etc/bash_completion.d/*一些例子。


130
包含与问题直接相关的简单示例如何?
MountainX

2
Ubuntu 16的实际脚本位于/usr/share/bash-completion/completions/<program>
peter

16
Imo,示例应包含在答案中,而不是链接中。
Billynoah '18

2
我相信这个平台应该是可以通过简单的Google搜索找到的完整文档的更实用的替代品。放弃文档链接无济于事。包含锚点的链接肯定不会有多大区别。
timuçin

4
The provided link has that already-今天可能会,但明天可能不会。或明年。或十年后。无论您对文档仍然有何建议,由于这些原因,Stack Overflow都不鼓励仅链接的答案。
利亚姆·道森

205

我迟了六个月,但我一直在寻找相同的东西,但发现了这一点:

您必须创建一个新文件:

/etc/bash_completion.d/foo

对于静态自动完成功能(例如--help/ --verbose),请添加以下内容:

_foo() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="--help --verbose --version"

    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _foo foo
  • COMP_WORDS 是一个包含当前命令行中所有单个单词的数组。
  • COMP_CWORD 是包含当前光标位置的单词的索引。
  • COMPREPLY 是一个数组变量,Bash从中读取可能的补全。

然后该compgen命令从中返回元素数组--help--verbose--version与当前单词匹配"${cur}"

compgen -W "--help --verbose --version" -- "<userinput>"

资料来源:http : //www.debian-administration.org/articles/316


27
这应该是公认的答案!这是一个完整的例子。
维克多·施罗德

5
提示:如果某人想要不以单词开头的单词的建议,-而不必开始输入目标单词就显示它们,则只需删除if [...] thenfi行即可。
Cedric Reichenbach

1
这是我一直在寻找了几个小时的确切答案,事实证明,它只是淹没在一些复杂的文档中,而从未提及该文件应放在中/etc/bash_completion.d/。我之所以来到这里,是因为我想在某个地方发布答案,但事实证明,有人一直领先三年:)清晰,简洁,完整的示例,谢谢!
SteenSchütt'16


34

所有bash补全都存储在中/etc/bash_completion.d/。因此,如果要使用bash_completion构建软件,则值得deb / make install在该目录中放置一个文件,其中包含软件名称。这是Rsync的bash完成脚本示例:

# bash completion for rsync

have rsync &&
_rsync()
{
    # TODO: _split_longopt

    local cur prev shell i userhost path   

    COMPREPLY=()
    cur=`_get_cword`
    prev=${COMP_WORDS[COMP_CWORD-1]}

    _expand || return 0

    case "$prev" in
    --@(config|password-file|include-from|exclude-from))
        _filedir
        return 0
        ;;
    -@(T|-temp-dir|-compare-dest))
        _filedir -d
        return 0
        ;;
    -@(e|-rsh))
        COMPREPLY=( $( compgen -W 'rsh ssh' -- "$cur" ) )
        return 0
        ;;
    esac

    case "$cur" in
    -*)
        COMPREPLY=( $( compgen -W '-v -q  -c -a -r -R -b -u -l -L -H \
            -p -o -g -D -t -S -n -W -x -B -e -C -I -T -P \
            -z -h -4 -6 --verbose --quiet --checksum \
            --archive --recursive --relative --backup \
            --backup-dir --suffix= --update --links \
            --copy-links --copy-unsafe-links --safe-links \
            --hard-links --perms --owner --group --devices\
            --times --sparse --dry-run --whole-file \
            --no-whole-file --one-file-system \
            --block-size= --rsh= --rsync-path= \
            --cvs-exclude --existing --ignore-existing \
            --delete --delete-excluded --delete-after \
            --ignore-errors --max-delete= --partial \
            --force --numeric-ids --timeout= \
            --ignore-times --size-only --modify-window= \
            --temp-dir= --compare-dest= --compress \
            --exclude= --exclude-from= --include= \
            --include-from= --version --daemon --no-detach\
            --address= --config= --port= --blocking-io \
            --no-blocking-io --stats --progress \
            --log-format= --password-file= --bwlimit= \
            --write-batch= --read-batch= --help' -- "$cur" ))
        ;;
    *:*)
        # find which remote shell is used
        shell=ssh
        for (( i=1; i < COMP_CWORD; i++ )); do
            if [[ "${COMP_WORDS[i]}" == -@(e|-rsh) ]]; then
                shell=${COMP_WORDS[i+1]}
                break
            fi
        done
        if [[ "$shell" == ssh ]]; then
            # remove backslash escape from :
            cur=${cur/\\:/:}
            userhost=${cur%%?(\\):*}
            path=${cur#*:}
            # unescape spaces
            path=${path//\\\\\\\\ / }
            if [ -z "$path" ]; then
                # default to home dir of specified
                # user on remote host
                path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null)
            fi
            # escape spaces; remove executables, aliases, pipes
            # and sockets; add space at end of file names
            COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
                command ls -aF1d "$path*" 2>/dev/null | \
                sed -e 's/ /\\\\\\\ /g' -e 's/[*@|=]$//g' \
                -e 's/[^\/]$/& /g' ) )
        fi
        ;;
    *)  
        _known_hosts_real -c -a "$cur"
        _filedir
        ;;
    esac

    return 0
} &&
complete -F _rsync $nospace $filenames rsync

# Local variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indent-comment: t
# indent-tabs-mode: nil
# End:
# ex: ts=4 sw=4 et filetype=sh

检查其中最与您的程序最匹配的bash完成文件之一可能是值得的。最简单的示例之一是rrdtool文件。


2
我们可以配置完井以从其他位置加载吗?IE浏览器 〜/ .local
克里斯(Chris)

1
是的,你可以把这样的文件,任何你想要的,然后把source ~/.local/mycrazycompletion你的~/.bashrc
斯蒂法诺宫


如今,大多数pkg-c补全位于命令onfig --variable = completionsdir bash-completion`给出的目录中,该目录是上面链接的Bash Completion FAQ给出的建议。
佳诺

33

这是完整的教程。

让我们看一个脚本示例admin.sh,您希望在该脚本上自动完成工作。

#!/bin/bash

while [ $# -gt 0 ]; do
  arg=$1
  case $arg in
    option_1)
     # do_option_1
    ;;
    option_2)
     # do_option_1
    ;;
    shortlist)
      echo option_1 option_2 shortlist
    ;;
    *)
     echo Wrong option
    ;;
  esac
  shift
done

注意选项候选清单。使用此选项调用脚本将打印出该脚本的所有可能选项。

这里有自动完成脚本:

_script()
{
  _script_commands=$(/path/to/your/script.sh shortlist)

  local cur
  COMPREPLY=()
  cur="${COMP_WORDS[COMP_CWORD]}"
  COMPREPLY=( $(compgen -W "${_script_commands}" -- ${cur}) )

  return 0
}
complete -o nospace -F _script ./admin.sh

请注意,要完成的最后一个参数是要将自动补全添加到的脚本的名称。您需要做的就是将自动完成脚本添加为

source /path/to/your/autocomplete.sh

或将其复制到/etc/bash.completion.d


1
什么是prev变量?您似乎没有使用它。
dimo414 '17

@ dimo414看来是这样,我删除了该变量
kokosing

-o nospace选项有什么作用?
安德鲁·拉玛拉

11

如果您只需要一个简单的基于单词的自动完成功能(这样就不会完成子命令或执行任何其他操作),该complete命令就会有一个-W选项,可以正确执行操作。

例如,我有以下几行代码.bashrc可以自动完成名为jupyter的程序:

# gleaned from `jupyter --help`
_jupyter_options='console qtconsole notebook' # shortened for this answer
complete -W "${_jupyter_options}" 'jupyter'

现在jupyter <TAB> <TAB>为我自动完成。

gnu.org上的文档很有帮助。

它似乎依赖IFS正确设置的变量,但这对我没有造成任何问题。

要添加文件名完成和默认的BASH完成,请使用以下-o选项:

complete -W "${_jupyter_options}" -o bashdefault -o default 'jupyter'

要在zsh中使用此complete命令,请在您的命令中运行以下命令之前添加以下代码~/.zshrc

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi

我该如何使用它bash jupyter <TAB><TAB>
papampi

@papampi,我认为它仅在一个完成级别上起作用-我认为要在2层上完成,您需要上面更复杂的答案之一。另外,我最近阅读了有关bash完成的相当不错的教程。它并不能完全满足您的需求,但也许可以帮到您。祝好运!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.