版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
% }, G9 e. E/ d1 W: K9 J jgetopt/getopts:Bash中命令行选项/参数处理9 @3 A! F/ ?8 t' @6 F! s0 b
1 W; z1 v* S( V+ a$ \0.引言
% X }0 ]/ S1 C( ~8 z4 T8 _5 L1 z) t; I V' O5 o+ _5 a; E
写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。( R; P6 g. Q4 x3 M% c$ u# M
3 I) Z; }) y6 `+ z5 Y7 L选项与参数:
1 U6 M @8 u9 n/ z) C2 i9 w+ z) g
. m+ }4 J3 H. l# g如下一个命令行:' e* F- _: z$ [
3 D4 u' E- `. c/ I; P[vb]9 Y8 u! a) z3 N" \$ d( _0 F
./test.sh -f config.conf -v --prefix=/home3 E6 ?% p* w ^
6 h2 ]. \4 ~7 c7 | |6 _+ ~7 K我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。' v7 n9 m: K: B+ }
4 X0 p- T" c B% v
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。
# }- {1 Z7 B2 L6 _! K8 u: E
' a+ p8 Y9 p e% c5 m3 M/ G( R在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。
! o( O- ?8 f& N" B* U- i+ K, _2 h' A# }) s: ?
手工处理方式1 O) [2 @' w( i) x! R+ s
0 t( _4 I$ h0 Q1 q
getopts
9 [6 C5 O3 u2 D9 L( R2 b5 [* b% O
+ {6 h3 t- H6 K: y$ Lgetopt
: S6 P8 J1 V2 |. C- z% l, n- \
5 ~8 L# a: E7 V& i0 C: E下面我们依次讨论这三种处理方式。; n& [) a3 N* C% k$ U' l( @
4 P( p1 y& x6 {9 G6 u: u+ r1. 手工处理方式
7 O% [ z5 u! b+ x- O M2 u# U, f! A4 ]
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:, G5 c/ u- m- ?/ ]6 U K
/ D2 {' v5 B& N, E$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0] M6 [% ]( s1 o# g0 x$ b2 }* ^
$1 : -f,第一个参数.) z, x1 D9 ?7 R+ y, }* \
0 o0 m9 H8 t) @4 o$2 : config.conf
+ ~, F; }( Q% |+ r9 s- d8 \) y M9 q% ~) ^
$3, $4 ... :类推。% y6 K# L$ y5 G! n* r+ U. _1 a3 Q
. j* h5 }! V5 `/ ?1 I6 N3 H6 ]
$# 参数的个数,不包括命令本身,上例中$#为4. l& M* h- l% w4 g+ Y. Y! G
/ ~$ B& m/ ]3 I$ D7 u' d$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home& {. b8 z* Y% w2 [, N. C6 W& F
! j' g) S. o. y9 o5 c/ R$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:' p" R! U3 U5 O; m
3 Y0 _# h8 m8 r, p
[vb]
4 H3 i& t' i. p- d$ ?( W# g#!/bin/bash* L+ K/ s4 M% y- h8 C! m7 n
8 y6 @$ I% c' f! m8 F. ~for arg in "$*"
* k, ?1 L* G4 q/ U+ o& A8 F; }
, q1 O6 K2 y2 _7 \! c3 r9 S8 S4 M0 Fdo
! |8 k# C1 H4 f4 \
! Q$ q, [/ e/ N$ \$ [" Vecho $arg
& h9 G- X0 L" ~; ]' n) @4 l6 ~0 a$ h8 ^
done
9 w) T7 K( f z
2 n: t! y- A/ t1 Gfor arg in "$@". _ \; p( Y2 y, ]6 V1 f1 e7 S2 Q
: S w' I& E3 W6 T; A; k& L; edo
" c! e' y5 q$ B: f3 e; e( h5 A7 |
echo $arg9 M. X+ A) ]0 R7 ], y( }3 S, V2 x9 ~
q1 L/ U. x9 c0 y) A8 Ldone' V( m1 A/ u1 N# c5 Q9 N7 V. v/ ^6 u
" I$ }. O' p g9 ]/ C# Y0 N执行./test.sh -f config.conf -n 10 会打印:
% w/ S' K+ ^+ n1 E
+ d1 I+ L4 ?+ m- R-f config.conf -n 10 #这是"$*"的输出& m4 a+ S1 k; H
+ D) i$ U4 W6 V# M/ N+ a#以下为$@的输出
2 G9 s- T0 m3 M
; w* Z2 G9 V6 F2 q% H1 ?-f
+ g$ \3 Y& T8 c( v* S& ]6 D) p- _8 K# I# Y0 K, `+ t1 `/ _. T
config.conf
/ J$ D" b" P4 ]; ^. v1 }6 b/ A* J6 }6 m0 D. u: D$ w
-n: U* u- P( C1 L) S, J
; q" T3 i7 g. J5 S10
0 a' o- e2 |( c: |7 w' V! L) D1 {/ ~' M1 [* d
所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如, D# {: @ T. T% F
+ }" M: ` `5 y9 c
“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:
% r+ f' ~1 y" o) Z
/ i# @6 W* R: Y; Q[vb]
& H' [$ E; X( W#!/bin/bash
/ q# [2 e& b, C% v! l% S% f% _9 g# ?9 G. V6 `6 \
#if [ -n "$1" ],参数不为空
) j% F# ]+ g1 q3 }+ x4 j Z5 rif [ "$1" != "" ] * F( V7 v9 A% U0 z6 j' @$ ]7 Q
then
- w5 `$ d2 j. t2 d: `% c/ h. X# z" l, g4 U( x
#...有参数; [3 p, a* M! K/ n, c1 C+ S
" l5 s* j+ s7 Eelse0 R! q" X0 i- L! Z3 j; j4 E% h
+ F! R& B, v4 h! w" z8 c) M) athen5 C1 Y! v' D' f+ l, [$ V
1 P# D' F9 d" k; S" D#...没有参数& T g/ V7 k. ?! W* D# v- k8 H" a
7 O3 x1 i) f$ R I4 @
fi: z$ ?6 E, } ^9 T/ C L
2 D! Q& b. \( E+ t) _+ O- [% H
手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。" v* l' h$ g0 a% e7 T3 W! T6 r8 c" S
. Q( q6 g8 C3 }6 B' l
2. getopts/getopt. m+ T" ^& B! F# c( B/ y" s
6 e; E* j4 u5 _8 u+ u* ?5 g处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:/ {, \. k" M1 z0 z0 |) e. G
' w3 h# W3 i0 y$ s8 |9 c: @3 G./test.sh -a -b -c : 短选项,各选项不需参数) O( s5 g% R# i4 S" n( B- ?
3 L0 i0 x X$ K% M" D! A- ?
./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。" e2 h0 L, B. H
) r; S6 E+ e+ K3 d
./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。* e0 R4 D& R* x8 h9 u6 H
- ~% Q6 @" M# @9 t' Y+ p. ^/ X$ G./test.sh --a-long=args --b-long :长选项
% A0 b& ^& q% I2 ~* X0 V
0 [7 ^( O0 T S( P4 Q4 }我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh
4 `1 `* p5 H4 f U# [
+ k6 ^: K' m3 Y# _, l[vb]; N- R1 ^5 s; }3 _
#!/bin/bash
k' ?. x) f$ r0 `
J6 B9 J, O; r4 L) o6 B#选项后面的冒号表示该选项需要参数
4 |. s1 H1 ~ j1 S; M t M& f
# u+ U8 T0 X* y3 V& ?; ^$ W; Cwhile getopts "a:bc" arg& [2 D) ^* U" @
6 U1 b( K3 j: l/ x+ v8 k# }$ ^* fdo
4 b1 b6 T }# U! d6 B7 F
- q. a) _' q8 k4 l- z* i" G( Rcase $arg in
! I5 `, ]: R- `( ] A. O, h: @' S' r5 t1 E( }1 I
a)
1 d; E5 _- Y+ u( ?8 G; I! m9 O5 T
#参数存在$OPTARG中; z( J( A7 f& G* M5 S
+ \5 g. \5 `. X7 F. B/ s) T) v& `/ [4 uecho "a's arg:$OPTARG" ;;* D0 ?' J4 N/ }) D+ k# w
6 }! n% @; w5 w9 ^5 ~
b)$ x* S) k/ ?: ~. I' ]
9 l$ q2 F' T8 [: f) \' y
echo "b" ;;
- b, P, B: q6 \* ~6 g7 C1 V4 u4 [! Z; @( w
c)/ W( Y* N1 ]- L+ j2 q
) S. p+ N1 Q, c: R) J# V: z
echo "c" ;;" J* l$ O5 y( y+ D
. K7 t* I5 l# Y: J; r. V
?)
3 y# G1 L5 R; z" r4 n; N
# `, y/ g1 ?8 h% O# \9 R/ ]0 I) {#当有不认识的选项的时候arg为?' d, n3 O* @. C
5 G+ ` z2 V c2 q
echo "unkonw argument" exit 1 ;;
! a% U9 i% p: r1 i+ \1 O k# V9 Y7 @. A/ U7 E3 Z
esac
# e) j( B) q. T
; B+ f. ^: o5 [3 @6 S* ~) ydone
& z% l; A4 r$ p" ]
" P- B7 b) V! e' i* K+ A$ ?% F7 o$ g现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
+ @5 x0 j0 s% p9 N7 ]1 [% p/ g
. D7 c) P# D# R. D9 ~" ]来加载了。3 p0 ?6 t7 D$ A% ^4 N3 Z' h# B0 q
' F d$ J( }1 {; M# a4 C" `
应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:7 |5 p5 K2 j9 p; M
1 q( i$ e! B, c& o
[vb]
1 c% k! @! a+ t+ y5 U3 f$ h6 H3 Y#!/bin/bash+ p$ [! ^' ^ a$ V
' M7 x `9 v F# A small example program for using the new getopt(1) program.
; }7 H6 m# B5 W9 @
- v4 p" a' f9 c, C' m& j7 ]# This program will only work with bash(1)
, @) {' X# T( E' L
% w4 J# w. D6 J0 r# An similar program using the tcsh(1) script language can be found
" x: x6 ^( M7 w: Q( x" @' b
* ]) {5 [4 q) @+ r4 V# as parse.tcsh
1 k/ l J* n+ u7 [( ^2 j4 H: W+ t. v
# Example input and output (from the bash prompt):
% Y. b0 {$ m1 A+ J/ ~$ G" Q" S
! K/ o! F, a* c# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long " N" `# N) {0 u, U
- ]* `3 l' O( |( e" \# Option a; w9 }: i) ]; U5 X% |4 B8 l
p$ K9 v6 L- n& q s( D# Option c, no argument3 @: r) ]% i7 m% R3 @( g% D
! n( u3 b0 i7 D+ j6 m- x: w5 D P
# Option c, argument `more'
6 }6 F! B v3 p9 {3 I' Y' _/ G; [# c' M" i+ N) D
# Option b, argument ` very long '
. l) V/ t& H, J& T5 Y; g o
) S% Z$ r! y% r3 U# Remaining arguments:
( ?" g& @& B) j
, ]8 E! d+ x x# --> `par1'& t6 p1 B" i" X; n- R
( y6 r6 Y1 U% w4 N
# --> `another arg'
5 I0 Q1 {+ W& m: H8 p/ m& d- b( b2 ^3 c5 C
# --> `wow!*\?'
( N' q& f8 e9 L1 s# s Z3 T# w% O. z! G+ m, u U- Z
# Note that we use `"$@"' to let each command-line parameter expand to a1 O3 f8 q+ \' i, M( |% i" H
7 C1 I5 {, \8 K% T4 Y; p
# separate word. The quotes around `$@' are essential!7 E+ u$ L' a6 z, [- b8 ]
! ^0 `* B/ g% c% ~
# We need TEMP as the `eval set --' would nuke the return value of getopt.3 |# @% h5 r& m9 q3 r5 i( q" a
% ]/ @6 i1 ?6 Y* r& c5 J% ?#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
# O8 ^- P8 {2 `/ e, b6 `) E
: h# a& ^ h+ {7 K5 R#如-carg 而不能是-c arg
% S ^7 x5 L9 o, n
0 d% R: ?5 O$ ^ B) G V, D#--long表示长选项
5 K' y: j+ X: \* p, Z
0 ^1 l0 D" X4 G# p3 G#"$@"在上面解释过
/ w& |9 B" @6 H! _; x+ |2 x8 v9 b2 X0 f7 I- K8 v
# -n:出错时的信息
5 U8 `0 g n3 j5 H5 B( V, x. ?1 Y( D3 P; T" x2 c0 g
# -- :举一个例子比较好理解:* U: ^$ u7 z, ~& B
; C3 a" n( A, ]
#我们要创建一个名字为 "-f"的目录你会怎么办?
& f0 ?! P; Y0 N' Q6 I' l5 m( t' h. c+ y
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用
4 }4 m# X8 }. c; O% I, N3 d
$ \: v+ i1 V: K" t i# mkdir -- -f 这样-f就不会被作为选项。
! A4 O2 C: v& ^* C
& b7 @2 E1 u& m* j8 t$ k3 nTEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \6 Z: }/ s# J' ^( O
- W" y9 ]# u& h. j1 h5 ^" y I-n 'example.bash' -- "$@"`
5 i* v( P" L2 l3 e9 \
7 G( s+ x* {6 p- P0 yif [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
* M9 H& V" f f1 e- H# Note the quotes around `$TEMP': they are essential!/ a/ t: W7 h- g8 @( m
- x# A& O% ^; _5 a9 X
#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了- `. Q; H1 b" J& u0 {1 p
8 O1 i& c H2 ]2 meval set -- "$TEMP"
1 F$ ?- H$ n, X, f' H+ N' [; Z8 |+ s+ i/ |, v, a
#经过getopt的处理,下面处理具体选项。2 ^" n9 `' N$ k& D
+ u, Y% t6 f i _! D G
while true ; do
\1 F+ R5 y: \) T' x/ O9 x8 V( |) U8 {' h/ w% }' X+ s
case "$1" in& c* |( ^& Y4 j+ g# W
% a* C- b2 l6 C0 u4 f$ Y-a|--a-long) echo "Option a" ; shift ;;
4 x/ I& z- P$ B) d5 J3 Y/ m9 `4 H2 |# Z9 X! S; H
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;" a/ a% I0 O; }8 L
( q, e; o3 W2 H& | y) B-c|--c-long)
1 t- d* ]* r: U0 C6 |" D% d
+ {6 V9 Q- m3 u' ~# c has an optional argument. As we are in quoted mode,
7 H, X# }4 @7 c" u( M& c7 }' A8 @9 B) k7 O& k7 W8 |
# an empty parameter will be generated if its optional7 H6 R5 \1 C; r' z7 ~
6 w. J T1 c7 z9 i& q+ m. C5 V# argument is not found.7 V }/ h; _- Y6 O" I* [3 _
2 k) E+ w* C8 u' f' ^' f; E
case "$2" in/ p! j8 u3 |/ ]) k# U t. E. A
: G2 T' W$ e1 O% ^ a"") echo "Option c, no argument"; shift 2 ;;# J1 ]0 _5 h6 L4 H3 J4 D6 w
$ |, U6 B) h% `" S3 K3 p*) echo "Option c, argument \`$2'" ; shift 2 ;;
6 p3 }, D: O0 h z
" m9 p- O, ? a( g, v- i/ Desac ;;
/ J' b! v% _% S2 f ~; ^$ T7 t
O4 g- A G" w; d. E--) shift ; break ;;6 Q1 [$ I7 `% d
1 m. S0 P0 h# H
*) echo "Internal error!" ; exit 1 ;;1 T) r( ?- J3 o8 l) t" D
( w/ h' G" C+ X, x, W2 Y
esac
( ^. I5 ]) ]) S& l/ ^ r/ q
/ t1 D1 g' ?5 ]$ n1 G6 C+ b9 b4 |done
0 a, z0 T0 X$ P- S3 Q; U
/ h; b/ |2 }& q R9 e; v4 G1 Cecho "Remaining arguments:"
$ b @9 x3 n& s; Y. `3 A
6 F0 H3 u, {' ~* x6 wfor arg do3 W, R) b, t9 B7 f( K
7 x6 L& |" n' P8 R$ T( }
echo '--> '"\`$arg'" ;8 ^1 X# f j9 {8 \ `6 X0 \! L
" D3 ?* W+ c# e, g D
done
; C: \3 m9 A8 H( @, `# K
. X" S3 `, K( j+ I" d* O
4 p* g1 Y( O6 k4 \比如我们使用
* C. G. D# [6 b& W+ E; U' a7 Q7 {! N* E8 q: g- p: u
./test -a -b arg arg1 -c. O9 |8 W: y" H/ u% G2 N
: T! b% ?! R* G: y$ R& W" q9 z0 _你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
% R4 W" a P' H) W5 C
. U$ s7 Y( U+ d' |% r) ^; D' E# f-a -b arg -c -- arg1
% C7 ` Q7 J E) a% U! |3 X# c! _* k; V% k2 [" Y% \
$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。
7 |2 W2 A$ f: E" c& j( O0 {1 X) L6 s: N
3. 总结! |! K" b; Y5 Y$ q: D( R0 M( K
4 D- N) @0 b( I- `1 g+ ]. Q- p
豹尾。
; Q- T& x; B( ?3 a2 q; m
: R9 Q$ q4 ~1 @# |# V. |本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统0 U1 y/ K1 w1 {$ L9 ^( C8 N* F# a
, A: B l" H# |1 |# s( j1 I9 X |
|