如何在不显示此目录的情况下使用来自特定目录的文件路径自动完成bash命令行? [英] How to autocomplete a bash commandline with file paths from a specific directory without displaying this directory?
问题描述
您可以在此处找到相关问题:如何自动完成一个带有文件路径的bash命令行?
You can find a related question here: How to autocomplete a bash commandline with file paths?
我正在创建一个shell程序,它是一个命令行工具.我要为此工具创建自己的自动完成功能.
I am creating a shell program which is a command line tool. I want to create my own auto-completion for this tool.
对于--unit-test和-t选项,我想在可以运行my_app --directory的特定目录中自动完成文件路径.
For the options --unit-test and -t, I want to auto-complete on file paths from a particular directory which I can get running my_app --directory.
例如
运行:
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
(显示有关特定目录的建议,但不完整)
(display suggestions for specific directory without complete with it)
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
问题
我找不到办法.你有什么主意吗?
Problem
I can't find a way to do it. Do you have any ideas?
@ D'Arcy Nader建议
Suggested by @D'Arcy Nader
在 my_app_autocomplete
_directory=/absolute/path/to/the/directory/
,然后在compgen命令中替换变量
and then substitute the variable in the compgen command
# 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]
做
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]
做
user@computer:~$ my_app --unit-tests /absolute/path/to/the/directory/
它将删除 file.ext
部分.
问题:
- 我不想在命令行中添加特定路径
- 它会删除我在特定目录之后添加的内容,而不是自动完成该操作.
推荐答案
经过多次尝试和错误,我认为我已经解决了您的问题(也是我的问题):
After a lot of try and error, I think I got a solution to your problem (which was my problem as well):
_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
用于不是选项的参数(不是以-
开头).
I found this solution by looking at how cat
is autocompleted. It uses the _longopt
function, which in turn uses _filedir
for arguments that are not options (not beginning with -
).
现在,您可以为所需的每个目录声明一个完成函数,例如:
Now you can declare a completion function for each directory you need, like:
_complete_git_home_path() {
_complete_specific_path "${GIT_HOME}"
}
并将其附加到正确的命令上:
And attach it to the right commands:
complete -F _complete_git_home_path cdrepo lsrepo rmrepo cdwiki pyinst
或者在您自己的完成函数中使用它,以触发特定的选项,例如-unit-test
!
Or use it inside your own completion function, to trigger it for a specific option like --unit-test
!
这篇关于如何在不显示此目录的情况下使用来自特定目录的文件路径自动完成bash命令行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!