是否可以使bash自动完成看起来像在Cisco IOS shell中?
我的意思是为每个完成添加简短描述,如下所示:
telnet 10.10.10. (TAB Pressed)
10.10.10.10 - routerA
10.10.10.11 - routerB
其中10.10.10.10和10.10.10.11是可能的完成 和路由器A& routerB只描述(不执行)。
我知道bash可以用“完成-W”来完成命令,但是它能够为它们打印描述吗?
答案 0 :(得分:12)
我有一个解决方案,不需要按TAB超过两次或回应任何额外的信息。关键是检查是否只有一个完成,然后将完成除去到有效部分,通常是删除“注释”分隔符后面的最大匹配后缀。要完成OP的例子:
_telnet() {
COMPREPLY=()
local cur
cur=$(_get_cword)
local completions="10.10.10.10 - routerA
10.10.10.11 - routerB
10.20.1.3 - routerC"
local OLDIFS="$IFS"
local IFS=$'\n'
COMPREPLY=( $( compgen -W "$completions" -- "$cur" ) )
IFS="$OLDIFS"
if [[ ${#COMPREPLY[*]} -eq 1 ]]; then #Only one completion
COMPREPLY=( ${COMPREPLY[0]%% - *} ) #Remove ' - ' and everything after
fi
return 0
}
complete -F _telnet -A hostnames telnet
这会给出您正在寻找的确切输出,当只有一个可能的完成时,评论会在完成之前从中删除。
答案 1 :(得分:2)
我会根据候选人的数量(如@bonsaiviking所示)对简单案例使用转换,如果我需要更多的灵活性来展示用户,我会使用以下内容。
__foo () {
local WORDS
WORDS=("1|10.10.10.10|routerA" "2|10.10.10.11|routerB")
local FOR_DISPLAY=1
if [ "${__FOO_PREV_LINE:-}" != "$COMP_LINE" ] ||
[ "${__FOO_PREV_POINT:-}" != "$COMP_POINT" ]; then
__FOO_PREV_LINE=$COMP_LINE
__FOO_PREV_POINT=$COMP_POINT
FOR_DISPLAY=
fi
local IFS=$'\n'
COMPREPLY=($(
for WORD in "${WORDS[@]}"; do
IFS=\| read -ra SP <<<"$WORD"
if [ "${SP[1]:0:${#2}}" == "$2" ]; then
if [ -n "$FOR_DISPLAY" ]; then
printf "%-*s\n" "$COLUMNS" "${SP[0]}: ${SP[1]} - ${SP[2]}"
else
echo "${SP[1]}"
fi
fi
done
))
}
complete -F __foo x
注意:您可以使用COMP_TYPE
在Bash 4.x中设置FOR_DISPLAY
,但我也需要支持Bash 3.x.
表现如下:
$ x 1
标签
$ x 10.10.10.1
标签 标签
1: 10.10.10.10 - routerA
2: 10.10.10.11 - routerB
$ x 10.10.10.1
答案 2 :(得分:1)
是的,但你需要一点bash kung foo来构建这样的系统。完成通常的方式是将正常函数绑定到要完成的命令。您可以找到一些basic examples来更好地了解完成的工作原理,并开始开发完成功能。此外,如果您碰巧安装了bash-completion
软件包,则可以在系统中搜索当前在shell中完成的许多其他示例。
您还可以查看官方bash手册的completion section。
修改
我尝试了一些实验,现在我的结论是不能完全按照您的要求执行操作:bash不支持complete
结果旁边的帮助文字。你可以做的是添加提供的完整单词的图例。这可以在bash函数_myfoo
中用作complete -F _myfoo
,也可以在complete -C myfoo
上完成,在完成之前打印出图例。
主要的区别在于使用一个你被Bash绑定的函数,而命令可以用你选择的任何语言编写,只要它能够设置所需的环境变量。
这是一个小例子:
skuro$ touch ~/bin/myfoo
skuro$ chmod +x ~/bin/myfoo
skuro$ _myfoo(){
> echo "result1 -- number one"
> echo "result2 -- number two"
> local cur prev
> _get_comp_words_by_ref cur prev
> COMPREPLY=( $(compgen -W "result1 result2" "$cur") )
> return 0
> }
skuro$ complete -F _myfoo myfoo
skuro$ myfoo result<TAB>
result1 -- number one
result2 -- number two
result1 result2
答案 3 :(得分:1)
经过一番研究后,我找到了解决方案。我不知道它在思科看起来如何,但我知道它在Vyatta中是如何运作的。唯一的缺陷是,在此变体中,您必须按 TAB 3次才能获得第一次详细帮助(前两次打印正常完成)。一旦显示详细的帮助,下一个 TAB 将切换正常和详细的完成。
comment_show_last_detailed=1
comment_show_last_position=0
_comment_show()
{
local cur opts i opt comment opts comments
opts="result1
result2"
comments="comment1
comment2"
[ $comment_show_last_position -gt $COMP_POINT ] &&
comment_show_last_position=0
if [ $comment_show_last_detailed = 0 ] &&
[ $comment_show_last_position = $COMP_POINT ]; then
for ((i=1; ;++i)); do
opt=`echo "$opts" | cut -f$i -d$'\n'`
[ -z "$opt" ] && break
comment=`echo "$comments" | cut -f$i -d$'\n'`
echo
echo -n "$opt - $comment"
done
comment_show_last_detailed=1
COMPREPLY=
else
cur="${COMP_WORDS[COMP_CWORD]}"
SAVEIFS="$IFS"
IFS=$'\n'
COMPREPLY=( $(compgen -W "${opts}" ${cur}) )
IFS="$SAVEIFS"
comment_show_last_detailed=0
fi
comment_show_last_position=$COMP_POINT
}
complete -F _comment_show comment
我甚至设法使用COMP_TYPE
变量将 TAB 压缩减少到仅2,但是如果某些符号是bash,则存在bash不会在底线重新打印当前命令行的问题在第一次 TAB 按下后插入,因此有进一步研究的空间。
答案 4 :(得分:0)
灵感来自here
基本技巧是将帮助文本PS1(扩展)和原始命令打印到ncurses_init_pair(1, NCURSES_COLOR_YELLOW, NCURSES_COLOR_BLUE);
$x = 0;
foreach ($columns as $curcol) {
ncurses_wmove($win,1,$x);
ncurses_wcolor_set($win,1);
ncurses_wvline($win,NCURSES_ACS_VLINE,$termheight);
ncurses_wcolor_set($win,0);
$x+= $colwidth[$curcol]+3;
}
,然后将完成选项打印到stderr
。
以下是在bash中提供的代码段,类似于stdout
的完成功能。它将调用ruby脚本(称为telnet
)来生成实际的完成输出。
p.rb
这是_telnet_complete()
{
COMPREPLY=()
COMP_WORDBREAKS=" "
local cur=${COMP_WORDS[COMP_CWORD]}
local cmd=(${COMP_WORDS[*]})
local choices=$(./p.rb ${cmd[*]} --completions ${COMP_CWORD} ${PS1@P})
COMPREPLY=($(compgen -W '${choices}' -- ${cur} ))
return 0
}
complete -F _telnet_complete telnet
的实现:
p.rb
示例:
#!/usr/bin/env ruby
ip = ""
out_ps1 = []
out_args = []
state = :init
completion_req = false
ARGV.each do |e|
case state
when :init
if e == "--completions"
completion_req = true
state = :complte
else
out_args << e
if /^\d+\.\d+\.\d+\.\d+$/ =~ e
ip = e
end
end
when :complte
state = :ps1
when :ps1
out_ps1 << e
end
end
routes = {
"10.10.10.10" => "routerA",
"10.10.10.11" => "routerB",
}
if completion_req
$stderr.puts ""
routes.each do |k, v|
if k[0..ip.size] == ip or ip.size == 0
$stderr.puts "#{k} - #{v}"
$stdout.puts k
end
end
$stderr.write "#{out_ps1.join(" ")}#{out_args.join(" ")} "
exit 0
end