|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
8 v# m% h; U) M0 ]
概述, D- o" J+ L% o! u
bash 自动补全7 A5 u. S) V) y/ \3 A) x$ d
- 测试补全的脚本
- 参数自动补全
- 自定义补全& ]4 \5 f6 k; H% W2 G- n
zsh 自动补全# o& T v1 A! H" }4 }
- 参数自动补全
- 自定义补全! t: q; O q* n9 M: ?" m2 H: A
总结
/ j' q3 C9 q5 e! Z& h! G* c s( c
/ i2 Y2 W% L! H% f概述
6 q8 F7 O/ H( m, K4 {虽然CLI(命令行)类型的工具由于其高效,易定制的特性为很多人所喜爱(也包括我自己), 但是,相对于GUI工具,CLI工具给人的直观感觉就是不容易使用,如果看到工具中大量的参数说明后,更让人望而却步。
3 F# e# w- j8 z; B. ]0 M$ F4 ~1 |0 E# Y$ L2 f& A+ T! A
因此,如果在自己命令行工具中加入 自动补全 的功能,就可以极大的提高工具的易用性,还可以保留命令行工具原有的高效。 这里所说的 自动补全 不仅仅是补全那些固定的参数(这些意义不大),更多的是补全动态的内容。# P! m/ E: U3 o# N p. B# v
% @2 h2 n/ `) ^; h1 t9 x本篇主要介绍两种主流的 shell(bash 和 zsh)中,如何实现命令行工具的补全。9 E5 [- V2 \4 V+ W+ @7 M+ C
2 Z1 r8 a6 ]# _# n) u
bash 自动补全
; h) g9 ^. P4 ?2 M测试补全的脚本. F" B4 q' n4 C5 i
简单编写一个测试脚本用来测试后面的自动补全:. D+ _6 [/ g; ^+ K" X
7 |2 L3 \" ]/ V3 n6 ~#!/bin/bash
" |% w: f( y7 g" }3 d7 y" X# filename: cli-test.sh
4 r- N- M& O, l# Q# W3 [8 \
( d2 f/ r& C# EUPCASE=false# h7 E* m, O5 v" j4 k
DATE=""
; g* L9 ~3 A$ _9 S" }8 X7 h. H) U$ b9 a3 ~
usage() {% e7 L. G8 p/ B9 B- n7 R
echo "USAGE:", I2 y7 @7 E8 d( J7 I
echo "cli-test <options>"
% G; K9 N7 q3 C3 ]2 q0 _ echo " -h : print help"
. O, L4 }! W; b" s; ` echo " -u : print info upcase"& H( |# c- P+ z
echo " -p <xxx>: print info"' ]. j/ H! h" M/ n- Q! L r b
echo " -d <xxx>: date in print info"
# N3 ^5 L _6 n: Y7 Y}
1 |9 L7 _2 J' @: @5 }- ^; H& l6 Z2 ], ^; }; }' [
print() {, P3 R7 a$ f% C) @. C3 @, b. x% h. Z" v# w
if $UPCASE
* X. O. J$ P$ I/ \ Q then
6 q. }5 R- t3 N# k echo -n $1 | tr a-z A-Z
5 F% t. r5 _! G7 _& D6 O3 E else
; C) x c& n! O4 B" m; C echo -n $17 K1 L" G4 s# o6 v* a# ]
fi+ f( `. f' v. J) Z
. I# b; |9 V7 I h& v if [ "$DATE" != "" ]3 p3 K. E7 ?3 I n, q
then# \" q+ U3 p1 h+ C) b7 {
echo " date: $DATE"& F, n0 \0 c4 c" \! ^/ U3 ], I. g
else
- v$ `! F$ |# Q* l/ i! F9 } echo ""3 {2 Q2 o/ \0 ~4 | d
fi3 k% z* L) A* ?4 z5 h
}
2 h+ r, o! p! C. N. `* u+ |$ ]+ E3 m7 K. S v% X% Z8 @
while getopts "hup:d:" opt; do
: l" w: e- _+ {% [ case "$opt" in" T% R* N# u W. ^7 _: N
h)
, x* o4 k) i6 b H usage
* M% ]- _' G7 Y! ] exit 0
3 d$ I0 ]! K* L5 M5 F ;;3 i' s) P! G% ]/ D1 c
u)
8 C4 j% ^2 q/ }- o+ S' I. n4 J( q UPCASE=true4 E" B# [$ ~( ^8 U6 }% g
;;
. T- V2 }6 E. d6 {3 b P) H d)
4 f0 j% t4 W) a1 i5 j: y5 `) Z& k9 ^ DATE=$OPTARG+ l9 X7 h; i+ O9 p6 [
;;( Z0 ~5 [( f6 h2 P1 L
p)
$ L4 ~' T+ `1 Q, v6 V. h print $OPTARG- Y8 k5 ~$ @* e( o0 Q! S! o1 t$ O
;;
3 l* _+ {0 R" b esac
& O4 @& @( k5 E, }$ gdone6 y, C7 A& l; g# J8 P
测试上面的脚本如下:- h& _# X. \: f' W& [
; ~7 L) O. g9 ~+ U; p1 {, jbash-3.2$ chmod +x cli-test.sh0 ?6 n6 l3 L7 k9 s- Q( x: p2 T
bash-3.2$ ./cli-test.sh -h3 g0 }/ w* G6 z! ]
USAGE:. ?: x* _8 Q$ m4 u: ?" Z6 V$ Q
cli-test <options>
( i. v$ U9 ^3 Q0 Y, Q6 R -h : print help
, Y- z* p, J9 o& e4 R, x5 x -u : print info upcase
: r( P2 z4 ~3 u6 R( y -p <xxx>: print info
% i7 r5 n1 |5 `' ]: s -d <xxx>: date in print info
( U( ?' A- t$ L3 h6 l [bash-3.2$ ./cli-test.sh -p hello; i" I: Q2 \! M5 {3 O
hellobash-3.2$ ./cli-test.sh -p hello
" j3 k4 C) V8 ~$ S1 whello
; D- n J/ g* S- Hbash-3.2$ ./cli-test.sh -u -p hello' \" m% b) u; G% U
HELLO
+ k _1 Q* g1 O6 }) Hbash-3.2$ ./cli-test.sh -u -d 2016-10-13 -p hello
9 l/ `+ l) v9 e* }* Z: @; THELLO date: 2016-10-13& X+ O/ i. o2 g; K j' d
6 g3 t6 R3 M1 P h
. S ^# r& Q5 u参数自动补全
3 |- R* M( W) s. a$ z# t3 _7 _参数的补全一般来说比较简单,因为一个命令行工具的参数一般都是固定的。 下面的参数补全脚本是针对 上面的 测试补全的脚本 cli-test.sh4 N% n, o5 K: _3 X
( l7 ^" t; H6 ^1 P1 W
_complete_func() {! X, J! _5 k @" ~' R: `" L2 ^, I
local cur prev opts base6 A- \5 |0 i' g/ c% b4 ^; R& I4 @. \
COMPREPLY=()0 d, r' e; Q* v! z, w$ D
cur="${COMP_WORDS[COMP_CWORD]}"% O1 O8 P4 a& F4 u) M6 h1 {
prev="${COMP_WORDS[COMP_CWORD-1]}"/ u) S: }2 |! x- s5 T7 t4 G
/ c1 K ]% c7 H7 U opts="-h -u -d -p"
( }' t V) Q5 A
! o$ M/ |$ H4 j: {7 }2 @ if [[ ${cur} == -* ]] ; then+ `% r* s2 t* W; u
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
2 l0 y) w8 t9 j return 0
7 E0 q) k; `1 i7 W+ f fi0 e5 s* x G/ w; p5 ` H
* O9 F+ P& i. _1 ?
}9 k1 `; E) Q+ c+ \
+ q- f- L7 b& W5 `% Scomplete -F _complete_func cli-test.sh9 n8 P0 F1 b- [1 R; U3 ]
让自动补全脚本生效的方法如下:* g( X: y3 M* O; J
! s3 M: o' o6 c3 W
bash-3.2$ source bash_complete # 使自动补全脚本生效1 c5 ` i$ S0 B4 G3 }
bash-3.2$ ./cli-test.sh -<TAB><TAB> # 这里输入 - 之后,再输入2次<TAB>就可以把所有能补全的参数列出来$ x H% U+ |/ P
3 H, x4 Z( ]4 i' S( v& N3 u; x1 u. f2 q
/ K# s1 ^" A6 R) o3 n自定义补全
6 i7 Q/ ]8 Q8 Z上面的补全是补全固定的参数,简单,但是用处也不大,用户记不住的其实就是那些会变的参数内容。 下面尝试动态补全 cli-test.sh 的参数 -d 的内容(内容是当前日期以及前3天和后三天的日期) 修改 bash_complete 脚本如下:6 e! q. N8 l- k0 r# ~. p# A6 T, h
+ U% o( N0 J5 \! h. h
_complete_func() {
1 K& Z! m2 ]$ [, g. | local cur prev opts base
1 ^5 R1 k# k% Q; P4 c! E) n COMPREPLY=()
( ~" ?% d7 ]$ B @ cur="${COMP_WORDS[COMP_CWORD]}"
$ ?9 i5 H2 e) B$ q4 Z/ x prev="${COMP_WORDS[COMP_CWORD-1]}"
0 t; _; }4 C5 K# d
3 [& {3 X8 n3 V( U: x if [[ ${cur} == -* ]] ; then
) h n P' A) Z opts="-h -u -d -p". u% J3 t/ A! h# k& p9 h3 L
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )* A% c; a [( O# D6 ?4 C1 ^
else5 ~4 ~/ ?& x3 V0 s( F
opts=$( _complete_d_option ); l5 [( Y8 k+ J( D6 W; }
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) y0 Z& v0 U, M0 W2 J3 Y
fi
/ k8 e& `$ X5 @5 \8 F$ g+ ^
6 u; j6 n. M+ W) m" G( O. N return 0+ s7 M1 p4 M7 ?3 e" ?0 J% g
}
* ^% e4 F4 U5 E" u
1 i: E+ l+ f- y, J m_complete_d_option() {
" E3 r1 k% t# B4 Q date -v -3d +"%Y-%m-%d"
" q6 e0 h/ W" W7 }1 u5 J date -v -2d +"%Y-%m-%d"
" y: Z/ u; Y y- e0 R( m# Z* ~) Q date -v -1d +"%Y-%m-%d"* e' t" m/ x: Z9 E+ T9 z9 L' D
date +"%Y-%m-%d"6 l9 D% W! O7 e7 i
date -v +1d +"%Y-%m-%d"0 m' ?1 U6 ^; `1 ?6 A& t( b
date -v +2d +"%Y-%m-%d"* W u, g! |6 Y3 b- o$ `
date -v +3d +"%Y-%m-%d"
& M( e# @; c( H6 |, ?}7 d! |# ^4 i) J V( p3 X( o3 h- ]: W/ O
8 @5 H6 ] _" g' j& _
complete -F _complete_func cli-test.sh7 @1 O( S0 ]/ z% a4 Z
测试动态补全的效果
, A9 _' E4 U: N. x0 A
" O) s1 m& N c' E, m# ibash-3.2$ source bash_complete # 使自动补全脚本生效5 y1 k! g9 a# I8 O; _$ n
bash-3.2$ ./cli-test.sh -u -d 2016-10-1<TAB><TAB> # 这是 2016-10-13 执行的结果,其他日子的结果会不一样8 V, N9 I3 ^/ G z0 z
2016-10-10 2016-10-11 2016-10-12 2016-10-13 2016-10-14 2016-10-15 2016-10-161 Y- c6 ?" z9 V7 q) }0 G
上面就是动态补全,_complete_d_option 函数就是用来实现动态补全的。3 |4 N. z5 A k
: y# q! j8 W9 f9 {7 @
, g: o4 p( J9 R
zsh 自动补全
7 e( Z- o/ _; L9 L' c相比于bash,zsh 的补全机制更加强大,也更加直观。 同样,下面也通过例子来演示如何在 zsh 中实现上面 bash 中同样的补全功能。
0 z1 D0 u* o. k, i; G1 R6 T% k
4 L. I& Q0 m& B, J8 p" F3 P5 S; }+ q/ Y1 f F" j; q
参数自动补全1 ^8 L1 x# J9 |* c
相比于 bash 的自动补全脚本,我觉得 zsh 的补全方式更加直观。. ~+ R9 [5 K; }. l! B( P' n1 I
4 F/ N, S% W& A6 i
#compdef cli-test.sh
# S( z/ {$ \0 N0 p- D2 c4 C# filename: _cli-test.h
! k" d7 i0 R( @0 ^2 L. B) u( K
. b. K& g9 h l4 v_cli_test() { P/ L4 q. T/ o, T$ Z# b3 v4 j
( p+ r/ u1 C2 m, y
_arguments -C -s -S \% [" \7 h' o$ F% h
'-h::' \
( |4 }8 T0 {0 _2 Y c '-u::' \' K# w5 P* V. j+ n2 [4 _: @; j" @( U
'-d::' \
6 c- E3 V# e. V1 w0 k" T1 p% K3 Q '-p::'
P" f0 ?& B) k}6 P( H0 }% b1 _2 P4 t
- ? T# q8 e3 c) S1 Q_cli_test "$@"% c. O5 U- ]# V8 Y, l6 E
zsh 中有个 fpath 的内置变量,将自动补全脚本放在 $fpath 中,或者在 $fpath 中创建指向自动补全的脚本的软连接都可以。 下面是我的环境中 fpath 的值
+ _4 _' O2 A9 Y* c: E7 K/ e
, |) _. k; s. Y3 T. F9 g' z$ echo $fpath( K" g/ C0 [% G0 N
/usr/local/share/zsh/site-functions /usr/share/zsh/site-functions /usr/share/zsh/5.0.8/functions
3 s: O: m& d! g$ m, ^$ J为了测试 zsh 下自动补全是否有效,我在 fpath 下给自己的自动补全脚本创建了软连接- S; J" D5 `- a" D: u4 R
: r: y, W. \& P, Q8 o$ I
$ cd /usr/local/share/zsh/site-functions
# ^3 u; |+ P+ K) P; ?$ ln -s ~/projects/bash/autocomp/_cli-test.sh _cli-test.sh
0 f. E+ h6 Z* E7 l+ C' R" g" w; r测试结果
$ ^3 D2 D! H& S% b/ q1 J# G, q8 x
$ ./cli-test.sh -<TAB><TAB>
9 T0 @6 E& P1 m9 H+ d-d -h -p -u
3 x7 o4 @/ _) M可以看出,zsh 的补全方法非常简单直观。稍微解释下上面的代码: Y; b6 l2 [- j- Y; a2 f
0 R) S n4 \6 e; C
_arguments
/ B# A6 C# Y! W9 e9 h7 _$ h这个函数是 zsh 自带的,有点类似 bash 中的 compgen ,但是功能更加强大。8 p/ d$ ^0 F# v
6 S" ]) B: ~1 h0 u" ~'-h::' \
4 e! m d/ J' D1 r2 ?! C& @这里 : 分割的3部分分别是 “待补全的参数:参数的说明:动态补全参数的内容“
% M" e! T# _( @/ ] ~; [) C6 y
8 t0 ]; B4 V1 t- I' b# M
& e: f9 X/ S" F" r自定义补全# j8 Q( F2 A! c: ^. T/ B1 j, c
根据上面的解释,要想动态补全 -d 参数非常简单,只要加个函数,并配置在 -d:: 之后即可
: p3 U/ T1 } T. d1 @8 c. {; a; O) ^# K( l
#compdef cli-test.sh
' a0 `! y% v+ F4 N( s# filename: _cli-test.h
* F+ |4 U" l2 \9 Y6 }1 J. ]
6 a5 m- [" I; S; R. V8 I_cli_test() {0 p, g9 U8 T1 X4 I
9 @% j) o* ?6 n1 B% {. Z, I5 w _arguments -C -s -S \+ R4 \6 v! v1 L% d* {
'-h::' \
& g* H# r# Z8 \ M Y. f6 { '-u::' \
4 b% D% `/ o: W3 o# { '-d:auto complete date:__complete_d_option' \9 u$ _( H+ M1 o% w8 J$ J& [
'-p::'% i+ F' \ A/ b# T
}
# O: R" ^1 Z, E/ U; n
9 r- e: W. A7 J' d& E, L: T. l__complete_d_option() {' F0 L3 C8 z1 w7 V4 y( L
local expl. E) c7 w) z2 r/ b" d
dates=( `generate_date` )$ N! R5 u% |; A, |! _1 r8 X& M @
: h: O: k( s2 V$ W1 V0 q+ j5 u _wanted dates expl date compadd $* - $dates8 ?$ ^& I) ] `
}
, @' o/ `: K+ X4 v5 O
R, R1 T, F$ ?( N" F" ygenerate_date() {- T% h2 `# n, U* p( N
date -v -3d +"%Y-%m-%d"
# v% R! y% Q4 d/ e9 P date -v -2d +"%Y-%m-%d"# N# n" b8 M) E) C& v8 I
date -v -1d +"%Y-%m-%d"9 c. p' N' q6 E/ i6 @' }8 p* l
date +"%Y-%m-%d"
) n# \! ]0 k5 f$ o date -v +1d +"%Y-%m-%d"$ {1 M% A! P+ F+ L0 [
date -v +2d +"%Y-%m-%d"8 N% ], t( b. i- N+ j
date -v +3d +"%Y-%m-%d"
/ o2 r+ `$ D. q- B}: T2 o, `8 y, {/ A. A
Z/ ?7 ^. e- d c
_cli_test "$@"1 x) l% B2 q; q! d/ M
测试动态补全的效果. h( _; B$ d, j1 M! p
5 E9 m Z, C$ R
$ ./cli-test.sh -u -d 2016-10-<TAB><TAB>& p2 t! |. y$ I! \% D9 Z
2016-10-14 2016-10-15 2016-10-16 2016-10-17 2016-10-18 2016-10-19 2016-10-20
9 G L8 d" c, O: j
$ n& w; }, i. m5 P, a& A/ Z2 a9 g5 B& W' W7 n, K+ P
总结; ?: A9 ^: H6 t9 ^
2中shell环境下的自动补全都介绍完了,它们自动补全的机制都不难,只是 zsh 毕竟是新一点的shell,补全方式更加简单易懂。 特别是对于存在子命令和复杂的参数补全,以及参数内容动态补全的情况下,zsh 的机制更加易于维护。/ s4 F" w) l8 _: _
|
|