如何使用来自特定目录的文件路径自动完成bash命令行而不显示此目录?

时间:2017-06-09 08:56:25

标签: linux bash autocomplete

您可以在此处找到相关问题:How to autocomplete a bash commandline with file paths?

上下文

我正在创建一个shell程序,它是一个命令行工具。我想为这个工具创建自己的自动完成功能。

对于选项--unit-test和-t,我想在特定目录的文件路径上自动完成,我可以运行my_app --directory。

e.G。

生成

user@computer:~$ my_app --install [TAB][TAB]

会做:

Public/          bin/                 Desktop/              
Documents/       Music/               Downloads/
user@computer:~$ my_app --install 

(显示当前目录)

生成

user@computer:~$ my_app --unit-tests [TAB][TAB]

会做:

folder/              folder2/             folder3/
.hidden_file         file.extension       file2.extension
user@computer:~$ my_app --unit-tests 

(显示特定目录的建议,但未完成)

my_app_autocomplete文件

__my_app_autocomplete()
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="--help -h --install -i --run -r --rebuild -rb --show-running-containers -ps --stop -s --remove -rm --logs -l --bash -b --sass -css --unit-tests -t"
    containers="nginx php mysql mongo node"
    sass="watch"

    # By default, autocomplete with options
    if [[ ${prev} == my_app ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
    # By default, autocomplete with options
    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
    # For --install and -i options, autocomplete with folder
    if [ ${prev} == --install ] || [ ${prev} == -i ] ; then
        COMPREPLY=( $(compgen -d -- ${cur}) )
        return 0
    fi
    # For --stop --remove --logs and --bash, autocomplete with containers
    if [ ${prev} == --stop ] || [ ${prev} == -s ] || [ ${prev} == --remove ] || [ ${prev} == -rm ] || [ ${prev} == --logs ] || [ ${prev} == -l ] || [ ${prev} == --bash ] || [ ${prev} == -b ] ; then
        COMPREPLY=( $(compgen -W "${containers}" -- ${cur}) )
        return 0
    fi
    # For --sass and -css, complete with sass options
    if [ ${prev} == --sass ] || [ ${prev} == -css ] ; then
        COMPREPLY=( $(compgen -W "${sass}" -- ${cur}) )
        return 0
    fi
    # For --unit-tests and -t, complete from a specific folder
    if [ ${prev} == --unit-tests ] || [ ${prev} == -t ] ; then
        COMPREPLY=( $(compgen -d -- ${cur}) )
        return 0
    fi
}
complete -o filenames -F __my_app_autocomplete my_app

问题

我无法找到办法。你有什么想法吗?

调查

使用包含特定目录的变量

@ D' Arcy Nader建议

my_app_autocomplete

的开头添加
_directory=/absolute/path/to/the/directory/  

然后在compgen命令中替换变量

# For --unit-tests and -t, complete with relative to my_app folder paths
if [ ${prev} == --unit-tests ] || [ ${prev} == -t ] ; then
    COMPREPLY=( $(compgen -d -- "${_directory}") )
    return 0
fi

行为:

生成

user@computer:~$ my_app --unit-tests [TAB][TAB]

DO

user@computer:~$ my_app --unit-tests /absolute/path/to/the/directory/

它添加了目录的路径。

生成

user@computer:~$ my_app --unit-tests /absolute/path/to/the/directory/file.ext[TAB][TAB]

DO

user@computer:~$ my_app --unit-tests /absolute/path/to/the/directory/

删除file.ext部分。

问题:

  • 我不想在命令行中添加特定路径
  • 删除我在特定目录后添加的内容,而不是自动完成它。

3 个答案:

答案 0 :(得分:1)

经过大量的尝试和错误,我认为我找到了解决问题的方法(这也是我的问题):

_complete_specific_path() {
  # declare variables
  local _item _COMPREPLY _old_pwd

  # if we already are in the completed directory, skip this part
  if [ "${PWD}" != "$1" ]; then
    _old_pwd="${PWD}"
    # magic here: go the specific directory!
    pushd "$1" &>/dev/null || return

    # init completion and run _filedir inside specific directory
    _init_completion -s || return
    _filedir

    # iterate on original replies
    for _item in "${COMPREPLY[@]}"; do
      # this check seems complicated, but it handles the case
      # where you have files/dirs of the same name
      # in the current directory and in the completed one:
      # we want only one "/" appended
      if [ -d "${_item}" ] && [[ "${_item}" != */ ]] && [ ! -d "${_old_pwd}/${_item}" ]; then
        # append a slash if directory
        _COMPREPLY+=("${_item}/")
      else
        _COMPREPLY+=("${_item}")
      fi
    done

    # popd as early as possible
    popd &>/dev/null

    # if only one reply and it is a directory, don't append a space
    # (don't know why we must check for length == 2 though)
    if [ ${#_COMPREPLY[@]} -eq 2 ]; then
      if [[ "${_COMPREPLY}" == */ ]]; then
        compopt -o nospace
      fi
    fi

    # set the values in the right COMPREPLY variable
    COMPREPLY=( "${_COMPREPLY[@]}" )

    # clean up
    unset _COMPREPLY
    unset _item
  else
    # we already are in the completed directory, easy
    _init_completion -s || return
    _filedir
  fi
}

我通过查看cat如何自动完成来找到此解决方案。它使用_longopt函数,而_filedir函数又使用-作为非选项的参数(不以_complete_git_home_path() { _complete_specific_path "${GIT_HOME}" } 开头)。

现在,您可以为所需的每个目录声明一个完成函数,例如:

complete -F _complete_git_home_path cdrepo lsrepo rmrepo cdwiki pyinst

并将其附加到正确的命令:

--unit-test

或者在您自己的完成功能中使用它,以触发#standardSQL WITH SampleDates AS ( SELECT DATE '2015-12-31' AS d UNION ALL SELECT DATE '2016-01-01' AS d UNION ALL SELECT DATE '2016-12-31' AS d UNION ALL SELECT DATE '2017-01-01' AS d UNION ALL SELECT DATE '2017-01-02' AS d UNION ALL SELECT DATE '2017-02-28' AS d UNION ALL SELECT DATE '2017-12-31' AS d UNION ALL SELECT DATE '2018-01-01' AS d UNION ALL SELECT DATE '2018-12-30' AS d UNION ALL SELECT DATE '2018-12-31' AS d UNION ALL SELECT DATE '2019-01-01' AS d UNION ALL SELECT DATE '2019-01-07' AS d ) SELECT d as date, EXTRACT(YEAR FROM d) AS year, FORMAT_DATE('%a', d) AS week_day, FORMAT_DATE('%V - %G', d) AS iso_week, FORMAT_DATE('%W - %G', d) AS W_G FROM SampleDates ORDER BY date; Row date year week_day iso_week W_G 1 2015-12-31 2015 Thu 53 - 2015 52 - 2015 2 2016-01-01 2016 Fri 53 - 2015 00 - 2015 3 2016-12-31 2016 Sat 52 - 2016 52 - 2016 4 2017-01-01 2017 Sun 52 - 2016 00 - 2016 5 2017-01-02 2017 Mon 01 - 2017 01 - 2017 6 2017-02-28 2017 Tue 09 - 2017 09 - 2017 7 2017-12-31 2017 Sun 52 - 2017 52 - 2017 8 2018-01-01 2018 Mon 01 - 2018 01 - 2018 9 2018-12-30 2018 Sun 52 - 2018 52 - 2018 10 2018-12-31 2018 Mon 01 - 2019 53 - 2019 11 2019-01-01 2019 Tue 01 - 2019 00 - 2019 12 2019-01-07 2019 Mon 02 - 2019 01 - 2019 等特定选项!

答案 1 :(得分:0)

改进 @pawamoy answer

调用时:

_init_completion -s || return

如果 _init_completion 返回一个非空值,脚本将在没有执行 popd 命令的情况下退出,这可能会使您留在调用 pushd 时指定的目录中(但它甚至使我的终端崩溃!)。我建议改为这样做(请参阅 grouping commands 以了解 { } 解释)

_init_completion -s || { popd > /dev/null 2>&1; return; }

此外,如果您的目标是可移植性,&> 重定向是不可移植的,因为它不是官方 POSIX shell 规范 (see this answer) 的一部分,您应该使用

> /dev/null 2>&1

代替

&> /dev/null

答案 2 :(得分:-1)

替代

COMPREPLY=( $(compgen -d -- "${_directory}") )  

COMPREPLY=( $(compgen -f -- "${_directory}" | sed "s/${_directory}//g" ) )