如何创建具有自动完成功能的脚本?

UAd*_*ter 158 bash auto-completion

当我使用类似程序svn并在 Gnome 终端中输入时:

svn upd
Run Code Online (Sandbox Code Playgroud)

并点击Tab它自动完成:

svn update
Run Code Online (Sandbox Code Playgroud)

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

小智 244

您必须创建一个新文件:

/etc/bash_completion.d/foo
Run Code Online (Sandbox Code Playgroud)

对于静态自动完成(例如--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
Run Code Online (Sandbox Code Playgroud)
  • COMP_WORDS 是一个包含当前命令行中所有单个单词的数组。
  • COMP_CWORD 是包含当前光标位置的单词的索引。
  • COMPREPLY 是一个数组变量,Bash 从中读取可能的补全。

compgen命令返回来自 的元素数组--help--verbose--version与当前单词匹配"${cur}"

compgen -W "--help --verbose --version" -- "<userinput>"
Run Code Online (Sandbox Code Playgroud)

来源

  • 提示:如果有人想要不以 `-` 开头的单词的建议,并且无需开始输入目标单词就可以显示它们,只需删除 `if [...] then` 和 `fi` 行。 (7认同)
  • 教程 - [创建 bash 完成脚本](https://iridakos.com/tutorials/2018/03/01/bash-programmable-completion-tutorial.html) (3认同)

小智 52

这是一个完整的教程。

让我们举一个admin.sh您希望自动完成工作的脚本示例。

#!/bin/bash

while [ $# -gt 0 ]; do
  arg=$1
  case $arg in
    option_1)
     # do_option_1
    ;;
    option_2)
     # do_option_2
    ;;
    shortlist)
      echo option_1 option_2 shortlist
    ;;
    *)
     echo Wrong option
    ;;
  esac
  shift
done
Run Code Online (Sandbox Code Playgroud)

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

在这里你有自动完成脚本:

_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
Run Code Online (Sandbox Code Playgroud)

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

source /path/to/your/autocomplete.sh
Run Code Online (Sandbox Code Playgroud)

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

  • 答案中的路径看起来已经过时,并且采购基本上是不必要的。用户完成脚本来自`~/.local/share/bash-completion/completions/`;该目录可以由用户创建。系统完成脚本一般存储在`/usr/share/bash-completion/completions/`中。 (3认同)

Flo*_*sch 47

您可以使用可编程完成。看看/etc/bash_completion/etc/bash_completion.d/*一些例子。

  • 包含一个与问题直接相关的简单示例怎么样? (166认同)
  • Imo,示例应包含在答案中,而不是包含在链接中。 (26认同)
  • `所提供的链接已经有了` - 今天可能有,但明天可能没有。或者明年。或者十年后。无论您对文档的相关性提出什么建议,Stack Overflow 都出于这些原因不鼓励仅提供链接的答案。 (8认同)
  • Ubuntu 16 的实际脚本位于`/usr/share/bash-completion/completions/&lt;program&gt;` (5认同)
  • 我相信这个平台应该是一个更实用的替代品,可以通过简单的谷歌搜索找到完整的文档。转储文档链接无济于事。包含锚点的链接肯定没有太大区别。 (4认同)

Mar*_*ppi 35

所有 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
Run Code Online (Sandbox Code Playgroud)

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

  • 我们可以配置完成以从其他位置加载吗?IE。~/.local (2认同)
  • 答案中的路径似乎已过时。根据[此常见问题解答](https://github.com/scop/bash-completion/blob/master/README.md),用户完成脚本源自`~/.local/share/bash-completion/completions/ `; 该目录可以由用户创建。系统完成脚本一般存储在`/usr/share/bash-completion/completions/`中。 (2认同)

Ben*_*Ben 21

如果你想要的只是一个简单的基于单词的自动完成(所以没有子命令完成或任何东西),那么complete命令有一个-W选项可以做正确的事情。

例如,我在我.bashrc的自动完成一个名为jupyter的程序中有以下几行:

# gleaned from `jupyter --help`
_jupyter_options='console qtconsole notebook' # shortened for this answer
complete -W "${_jupyter_options}" 'jupyter'
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

complete -W "${_jupyter_options}" -o bashdefault -o default 'jupyter'
Run Code Online (Sandbox Code Playgroud)

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

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi
Run Code Online (Sandbox Code Playgroud)