|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
! o4 O ]" x0 s概述
9 |! I- W0 H) c4 g( B6 ^* Z' Lbash 自动补全
$ ~2 j/ _2 g) K' I9 r/ M5 D7 w- 测试补全的脚本
- 参数自动补全
- 自定义补全
, D$ J+ U0 e7 u zsh 自动补全! g7 p3 w O/ h& C( ^( ~
- 参数自动补全
- 自定义补全6 A8 v$ m; ]1 G* R. ]
总结( O; H! T; {6 a2 _3 t! K
. V+ F3 k8 S. @; `# z) V$ ?概述
4 O4 h* b' D3 L& L8 _虽然CLI(命令行)类型的工具由于其高效,易定制的特性为很多人所喜爱(也包括我自己), 但是,相对于GUI工具,CLI工具给人的直观感觉就是不容易使用,如果看到工具中大量的参数说明后,更让人望而却步。* {5 Y3 Y' z3 |' f, ?
0 `: g! e) f2 C5 y2 A
因此,如果在自己命令行工具中加入 自动补全 的功能,就可以极大的提高工具的易用性,还可以保留命令行工具原有的高效。 这里所说的 自动补全 不仅仅是补全那些固定的参数(这些意义不大),更多的是补全动态的内容。- W5 P) X9 C; o, [: f$ H8 O
4 N: E6 G, h8 p% f7 m3 q3 O本篇主要介绍两种主流的 shell(bash 和 zsh)中,如何实现命令行工具的补全。
- k% w% r: H# o/ f7 [0 l* ^/ E/ q) Z/ `/ G( w
bash 自动补全0 A4 o" ?; o# p6 J" G
测试补全的脚本
5 I' x& d( Z. A) j0 Y9 \简单编写一个测试脚本用来测试后面的自动补全:
R3 C9 ?4 a N1 _3 Y, _
S1 ?/ J6 {8 r#!/bin/bash
1 o" f$ C5 P' ?# filename: cli-test.sh4 d5 n: _) ?0 Q' ~
2 S% D% X' n: Z4 ^$ p, b
UPCASE=false
) i9 |- L+ T7 D4 t$ f! NDATE=""
% ^1 D) c, N2 x" x, s* j, i
( p6 ~! t N' u' v) C# Qusage() {
- v+ r. W+ e6 x: p; q* X' G echo "USAGE:"
' t$ ~& _5 l/ l m' O/ F echo "cli-test <options>"
" k1 s5 k2 b+ _+ p echo " -h : print help"
! L4 U1 n+ Y0 o! ?* Y8 j echo " -u : print info upcase"
( }+ D: [1 j- m7 ?2 h8 G3 G echo " -p <xxx>: print info"4 z+ F* P2 C1 O7 h5 ?' u3 `
echo " -d <xxx>: date in print info"
) A% D: \/ t) F7 r* D}
% M) Z2 s7 I' d: p0 C2 Z
1 w* N: V9 x, i* c2 n1 Lprint() {
1 q8 j8 M! X7 H: i. L3 n w if $UPCASE
& r5 c$ i2 W3 J$ Q; T. V then
8 S2 I- |8 l$ o. { x' q3 k: t echo -n $1 | tr a-z A-Z+ F; }4 `! @: ~' u( s6 t1 Z" o
else
5 o) \# q; V% Q. s4 U echo -n $1( `- o6 n6 A6 h* q0 r5 ~9 l8 O) W
fi9 H% p. C$ L: Z2 ]. z
0 ]; U% ?5 Z# w4 k7 b- E, O if [ "$DATE" != "" ]
! ~6 g4 Y' y. l" j, [, K) L+ N: } then
: P. ~& e. M. d) d, `% F! m) B7 [ echo " date: $DATE"
, V' j1 C1 p+ @8 a* [ else- a* X, B: ^0 w/ s
echo "", k/ [( E+ b: {5 _
fi
( [2 M8 _9 Y3 Z4 w$ z+ x}
3 r) h7 Z! ~. q3 }* }
& I2 y5 i9 C; [0 ]9 ^) W; F1 Uwhile getopts "hup:d:" opt; do' N# h" \( q" z% L4 C1 _
case "$opt" in. h2 E+ S; Q# z1 C0 z; y5 T1 e
h)
) q W' I% P0 y5 L usage
$ Z R" {" u* H6 c7 d) k( k# O3 ~/ o exit 0
" N) }6 I& v0 o$ X ;;5 g! J0 q$ k, l( F
u)( q* v. k {* q, V: ]( r
UPCASE=true
$ i3 x6 }0 D+ O, _0 N8 A0 d: w ;;, Q5 {6 _/ v& |8 _
d), d/ i/ n/ }5 m7 a- K
DATE=$OPTARG0 S+ o9 o2 F9 ]" h* ?0 _$ {
;;
' q) N9 {& O3 \2 u$ a/ {. h) Z p)- ~1 N; t! _) Z2 P# ]
print $OPTARG& V( c1 u+ f' e: t8 k
;;' i+ O. k2 \7 W( q$ C- l! d
esac. d0 X' h& D9 [6 {
done
: v4 f& k: R! a: I测试上面的脚本如下:& T$ n+ O- [* ~' c8 a2 `
; t$ Q* `* s9 Y, J
bash-3.2$ chmod +x cli-test.sh$ U1 D: {3 @' M' c9 H
bash-3.2$ ./cli-test.sh -h
6 m! y) H4 z5 u0 |USAGE:
3 B7 y2 G) G; |. z: ecli-test <options>
& A1 z! v' Z: ? ~& ~" F -h : print help
" O2 w% S' Z2 z* F1 F. @4 L -u : print info upcase0 O( a0 Q: h7 y
-p <xxx>: print info. Z( u% \1 |0 k& T4 q
-d <xxx>: date in print info$ T0 ]+ j) o+ O4 `
bash-3.2$ ./cli-test.sh -p hello. Q- \" A3 ]* |# m# G
hellobash-3.2$ ./cli-test.sh -p hello! ?. k3 O8 `- c: G! W
hello; r3 i2 i) z* n- m. X' Z# w
bash-3.2$ ./cli-test.sh -u -p hello# B |# I/ R; V/ L8 }0 W
HELLO
: H: V$ C, J, k2 Y( P8 o$ tbash-3.2$ ./cli-test.sh -u -d 2016-10-13 -p hello7 W% a2 J7 b0 H3 Z, l1 p7 U
HELLO date: 2016-10-13
. `1 p$ g- Z# A _. r K% P1 A: v1 o' r0 G }' G- a
, a& `- A1 q. A7 ^1 Q Z* A
参数自动补全
% E. g% B5 P* }/ }! r参数的补全一般来说比较简单,因为一个命令行工具的参数一般都是固定的。 下面的参数补全脚本是针对 上面的 测试补全的脚本 cli-test.sh
1 D- s; b, K1 X) z/ F9 c+ G2 Q w0 e$ d3 s1 e+ ?' `
_complete_func() {
9 \4 D- F# l. M$ j3 A/ E$ t5 s local cur prev opts base ~( V/ \* R6 T! S- D
COMPREPLY=()
# C3 u+ X5 e1 k) j8 O6 M+ y cur="${COMP_WORDS[COMP_CWORD]}"4 Z( a4 i5 E% K# Q! \7 j1 i' Y R
prev="${COMP_WORDS[COMP_CWORD-1]}"* P" a9 N/ J- j5 G
/ @* X+ B! W }! G9 N# ^ opts="-h -u -d -p"" Y! y1 ?, {5 z X
9 Z% a g% X: x& N. v/ U if [[ ${cur} == -* ]] ; then: D2 e# Z( n& m: t$ A( u$ }
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )6 O" r+ t: c. z) u: i
return 0/ D, P8 Z L0 @6 u
fi
3 d! ?0 V4 ^( s8 B) i! ?* P% M+ Y
; _, Q: ~5 T4 f- u, K( H}
. F) t) O1 `8 x# r& d# ^
" m7 y/ J2 n' o; I, _# u* fcomplete -F _complete_func cli-test.sh
, n4 y9 q4 N0 {7 D) }3 Y3 s让自动补全脚本生效的方法如下:0 k" G% r) w8 Y' [# L; v
; w9 k. W( i/ t6 `8 qbash-3.2$ source bash_complete # 使自动补全脚本生效
+ ?9 w' u' Z ]/ M( A- Zbash-3.2$ ./cli-test.sh -<TAB><TAB> # 这里输入 - 之后,再输入2次<TAB>就可以把所有能补全的参数列出来+ A' N) X0 f" E+ g$ W7 ?% T0 p% l
' M4 z2 ^3 I* q4 O3 L0 X+ h: o9 C: l$ C1 M7 v$ Y
; W' ]0 c$ z7 r. `+ }& Z0 U2 g
自定义补全. p0 T) v/ j5 V3 L* \. G. V% P
上面的补全是补全固定的参数,简单,但是用处也不大,用户记不住的其实就是那些会变的参数内容。 下面尝试动态补全 cli-test.sh 的参数 -d 的内容(内容是当前日期以及前3天和后三天的日期) 修改 bash_complete 脚本如下:% t d, s. d% }. b* Z' H( N
/ A- Q+ i- W9 E, }
_complete_func() {) d6 V3 {4 l. x( }, M. N0 Y
local cur prev opts base! a, J# W9 ~0 z+ p
COMPREPLY=()6 w. Q: P _; w' B% \' [' q
cur="${COMP_WORDS[COMP_CWORD]}"
: _: g g; \) U( c, _ prev="${COMP_WORDS[COMP_CWORD-1]}"$ g5 G8 f m1 M/ ~4 [
8 R# T" c {, _8 c& L( Y if [[ ${cur} == -* ]] ; then
+ l& k' c+ `: U+ a opts="-h -u -d -p"
/ }. h4 U; k- ~( x& |0 |& n: ~( @ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
* d" W7 S8 [. W* {- w( Q else
6 V. E) Y9 ^! A opts=$( _complete_d_option )
% W% ?! x$ N1 N" }: F9 A COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )/ N! B7 w$ j0 P! j$ H+ W3 b
fi# Q+ m# m! k- e- ]/ s
1 R7 O2 a% [7 `; F return 01 j$ S2 ?3 V) L) U9 E6 s' X
}7 J) f( H7 g% D8 Y* s: x4 U
+ x1 N( ~" h% n1 x
_complete_d_option() {3 l" `( E( c0 T" i5 e# V
date -v -3d +"%Y-%m-%d"
4 ^$ O6 m! [4 B6 j! r3 I2 B date -v -2d +"%Y-%m-%d"" U# \6 `7 m t) X4 j9 G+ I# j# ]
date -v -1d +"%Y-%m-%d") a' t% W I* ?) w3 a4 c2 N! B
date +"%Y-%m-%d"
3 a2 a6 h8 M& H/ U `0 M date -v +1d +"%Y-%m-%d"/ f, c: M- h2 T& R( ]
date -v +2d +"%Y-%m-%d"
+ D+ s! `0 A# v" l date -v +3d +"%Y-%m-%d"
+ I2 ^7 \! b: f+ {}# h7 _+ O* ^* \" m# n
5 o$ V. L8 \, F. [" Z# I4 G
complete -F _complete_func cli-test.sh$ ~0 E) r+ h9 `1 }! f
测试动态补全的效果
7 H. \. Q9 Q5 B1 {- b3 o( D A4 n# G/ g
bash-3.2$ source bash_complete # 使自动补全脚本生效7 s5 R* Z9 K8 n) j$ E. D# ^; w' H
bash-3.2$ ./cli-test.sh -u -d 2016-10-1<TAB><TAB> # 这是 2016-10-13 执行的结果,其他日子的结果会不一样
L' c, g( i; m$ j! V0 L- z2016-10-10 2016-10-11 2016-10-12 2016-10-13 2016-10-14 2016-10-15 2016-10-16% h G% G) c, C) X2 X- {* x
上面就是动态补全,_complete_d_option 函数就是用来实现动态补全的。
& `! _0 T6 z4 j- W ^# b, k- F% u4 [4 a# S4 l1 J% ^2 r
8 d2 C( ?3 N3 o# R: \zsh 自动补全
, B F2 d2 B6 S k相比于bash,zsh 的补全机制更加强大,也更加直观。 同样,下面也通过例子来演示如何在 zsh 中实现上面 bash 中同样的补全功能。
* d$ ^$ L3 z/ d$ q9 k$ P) y- R5 o% m# ^6 z
: _! f( s' S# [ I: i
参数自动补全
; x0 U1 c$ n5 m" I- l相比于 bash 的自动补全脚本,我觉得 zsh 的补全方式更加直观。, g. u! h# s) {, v; T2 ]
( Y( k6 a7 b/ J5 f3 q$ i' }#compdef cli-test.sh
$ g5 R8 D/ M* f) |1 w# Z# filename: _cli-test.h4 m; r" H, n6 q$ S; g2 ^3 D
( R. k1 H0 S0 r7 e0 C$ B. y T; O_cli_test() {0 a! z% l F8 k
% u% J3 t9 j3 y3 } W1 {
_arguments -C -s -S \
) g0 J4 B6 D) m" S '-h::' \
9 l) n8 J8 U' M- Q '-u::' \
- P3 ^% R6 T- L, F! L" s( g: K- V '-d::' \
3 }0 P3 V; z" Z0 {; C! U6 o6 | '-p::'
# P* U# l% `. E% n, {# n9 \7 h}! j* N6 a7 V2 S# x- n7 }+ Z' }
% }4 `6 c7 W2 p- J6 r_cli_test "$@"
' z u( j) T. X0 w3 dzsh 中有个 fpath 的内置变量,将自动补全脚本放在 $fpath 中,或者在 $fpath 中创建指向自动补全的脚本的软连接都可以。 下面是我的环境中 fpath 的值
& K5 s. q; Q; G& o+ G) h4 F! I, G& c* i3 ]
$ echo $fpath
: K& }" }4 Y6 `+ i/usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/5.0.8/functions% }$ S. j0 o g# d0 Q1 A, k
为了测试 zsh 下自动补全是否有效,我在 fpath 下给自己的自动补全脚本创建了软连接; N+ U9 t7 l" ]
8 _' D* |. k/ d+ g
$ cd /usr/local/share/zsh/site-functions
2 {" Q% V: z0 [" h9 U$ ln -s ~/projects/bash/autocomp/_cli-test.sh _cli-test.sh1 T9 r O, [1 i P M @
测试结果
- h$ Y D4 h+ T2 ?. I- j* i" E0 [" w0 I8 I, o) J+ ]
$ ./cli-test.sh -<TAB><TAB>
R+ P1 N* H. b. [0 f( z0 ^-d -h -p -u, u5 |! G; o' U l9 v& j; n# e! W0 k
可以看出,zsh 的补全方法非常简单直观。稍微解释下上面的代码% `' y3 K& o0 ~: @
) Z, ~4 {" \( j, E_arguments
( U* o& Z- B; m/ h9 [# k. v( z这个函数是 zsh 自带的,有点类似 bash 中的 compgen ,但是功能更加强大。
- Y8 m, N" X( _# w6 @- F/ v+ H& v
) T2 N" G' b' v'-h::' \
% z( |3 [# J+ b6 E" Z这里 : 分割的3部分分别是 “待补全的参数:参数的说明:动态补全参数的内容“, T2 G* P3 k$ o" e$ S3 R# H
0 b$ O/ G T3 K4 f: H) l" u! m9 t: W6 ^' D0 X
自定义补全% l: d4 a4 V9 j# t: y7 M0 Q
根据上面的解释,要想动态补全 -d 参数非常简单,只要加个函数,并配置在 -d:: 之后即可
9 }& U) m& R" k" b, c3 G8 u+ c+ F; U4 w7 d1 S( c
#compdef cli-test.sh
' o% {5 {) N- ?* R# filename: _cli-test.h
* ?+ z: {+ K: T1 ?( l7 C C' m! Z; K; B
_cli_test() {! w, j* \8 o7 g8 R3 B
' J9 T6 @. e" }% T/ V& S
_arguments -C -s -S \; l. f9 D4 @/ K9 u* B/ G
'-h::' \- [# G9 _3 D$ E0 R% _& E. w: k
'-u::' \! ~" ]: E! T5 U/ \9 k( A% r
'-d:auto complete date:__complete_d_option' \; U+ m: M) F+ S n2 h( \0 e
'-p::'' ~5 W& |6 t4 u
}! s8 s4 f+ {. q, y2 ^
' ~8 M! f8 ?4 |6 S__complete_d_option() {
" O6 i; Q; @3 A9 J1 V* z local expl
7 L2 U( [ G' T- c# n. z. K0 \; c dates=( `generate_date` )
+ s7 P. m; k9 S% b* g4 Q
6 w: [" H! l! E4 S, v G* x _wanted dates expl date compadd $* - $dates
& b& b5 ]7 I H6 N1 e+ S}
* r; @7 m& M; A, G" m, V5 w" q/ W R0 J4 ^# r0 l7 @* m$ `0 v9 t
generate_date() {
& V) _& e" U+ Q# K8 R date -v -3d +"%Y-%m-%d"3 `+ |% v# E4 L/ b1 K5 ] [9 h
date -v -2d +"%Y-%m-%d"# ?$ I* ^5 a3 @# F: q
date -v -1d +"%Y-%m-%d"* A. X# ~! c, S2 T- L' R) C* h& n
date +"%Y-%m-%d"
. O1 Y9 h. o! `; n: ~6 N9 q8 D2 A/ o date -v +1d +"%Y-%m-%d"
( c% S5 t0 r/ J- ], s) Q! j date -v +2d +"%Y-%m-%d"
* w# k, k$ t3 I" S; S% I1 i! j date -v +3d +"%Y-%m-%d"1 R* `) s3 W6 y) |0 ?; `/ R7 C
}% R- D% P% {7 i& Y0 W
6 |7 d4 D* o$ F$ R! W_cli_test "$@"! M3 s! O; z' J0 r4 \& G8 w7 I
测试动态补全的效果
; h- I- x9 M- L/ ^7 W# ]: y ^: d# a5 V4 O5 b
$ ./cli-test.sh -u -d 2016-10-<TAB><TAB>
+ P; j' n2 x5 M" \; {5 m( P2016-10-14 2016-10-15 2016-10-16 2016-10-17 2016-10-18 2016-10-19 2016-10-20+ K: m5 A; i; X
, f$ q l/ e8 X) b6 O$ q* ^# h3 v3 H% ?; A9 D. H
总结
# m2 Z) k( d9 L |5 n2中shell环境下的自动补全都介绍完了,它们自动补全的机制都不难,只是 zsh 毕竟是新一点的shell,补全方式更加简单易懂。 特别是对于存在子命令和复杂的参数补全,以及参数内容动态补全的情况下,zsh 的机制更加易于维护。
. U2 }6 i% U! m8 b' ` |
|