您可以在此处找到相关问题: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
部分。
问题:
答案 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)
调用时:
_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" ) )