版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
: N# N& l. O' ]5 |7 ^0 Hgetopt/getopts:Bash中命令行选项/参数处理( l( Q) p3 s% m9 p
( T+ y- T0 P# O: s
0.引言$ z) u/ M) w& P" l, V4 i
6 Z; e R6 ^# \! g; F3 a! R+ l写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
; }8 g4 s" B- b+ X6 P3 _1 X) A& p
. u% k9 a/ C6 H' ~. _- z! \选项与参数:
* u4 T! k% U9 g6 |( L/ n1 I! |! H2 K9 F2 B) N+ ^- g
如下一个命令行:
% X9 t$ e( {% S+ K3 o
1 Z& J1 t: x( `& Q( S+ y1 o[vb]" G+ M+ X2 i; s6 t
./test.sh -f config.conf -v --prefix=/home' u2 e5 U: u! T
# i5 m0 ^9 C' s q7 _
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。
6 K- L' K7 `/ b0 @* i4 g& o" k z0 M2 u& H5 F: P9 r' `
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。
* a2 F2 p& W; h+ n9 I7 ~
% T5 U- j1 `* y/ a3 K) k" l在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。( v4 P+ }; m4 t" |3 b3 H
; x6 k: H: H! m: M
手工处理方式
& ^8 w2 G5 t8 a3 g2 A* A& C# I8 h$ K5 w
getopts
+ ~4 q9 F& R% N/ h6 ?
' V# n, Q( I" u- z; Rgetopt
7 d" Y3 `! |1 f9 _0 n( ^4 M5 A% O( n) y! ~" G: g9 h
下面我们依次讨论这三种处理方式。
+ S+ @0 O5 T/ C6 K6 |& A9 ^( ^! g4 u% x
1. 手工处理方式3 w( |" k& D% k
% v9 q% k9 p+ h2 Z8 G, L a. c在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:5 X! C% [# G, E( |- r: E$ g
& ~# B$ ?4 f) s% f- M: d7 a9 c$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]
/ u8 V- o4 k+ B2 q$1 : -f,第一个参数.
, R( h) i6 v1 D' Q D2 G* @: u/ T1 \$ l2 ?- g# L+ [
$2 : config.conf3 e6 P" b6 @& d6 @$ z, c+ ~3 V
; d" _$ l7 E; e. k
$3, $4 ... :类推。
' Z8 s7 K X, M0 u) O& r
0 x9 B* R! E; Y7 I9 u) I9 O# t$# 参数的个数,不包括命令本身,上例中$#为4.$ `8 I6 O8 M# D6 C) W
5 G- h3 v7 j }$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home- ?9 J+ i; p5 s. u( ~
9 [7 r( ~, H6 W- s+ b( u6 g$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:5 `# C0 F/ k$ q* G# B/ b
4 i1 \+ d' a8 `. w- R$ b8 r[vb]5 C1 i0 }) b# E+ Z: q
#!/bin/bash
+ N- N: J, O8 {# f% f* J D3 I; \$ ^
for arg in "$*") C6 J% ?. u8 y' N0 O% T
, z, D/ m$ k& [9 s
do
! I, d: f q' @ }4 \6 A" p& w/ |' O: i2 o. _, I2 G7 P
echo $arg
" M* K5 l. x8 g' k* ?" D% T& F7 l: o0 l1 O8 x! t9 t. ~
done
# C6 o& b4 {4 {' q! O% v' i7 ^& e, r' D- D) i5 R& W$ O: j# |" }) r9 P+ i0 p& e
for arg in "$@"
# f4 L& V% I4 ~) c( f. a9 P
4 v8 R# R6 @4 M, t0 f3 B" R" vdo
w* t2 R! I5 C) H# ~) U. \
3 J' v8 O6 z- V0 u) X1 F+ l" }& hecho $arg
, J0 X6 y! V0 J4 I* U4 Z5 f# u# _- t5 O @1 o
done
* s% A" k/ n e. W
) ~2 V1 K4 I$ Q执行./test.sh -f config.conf -n 10 会打印:2 |4 _ `/ T* d3 `- s: r
: `$ C1 {) |5 G-f config.conf -n 10 #这是"$*"的输出
% A9 u# y! E" X
, ?0 f* b1 l* }6 L3 G- \#以下为$@的输出, W& F1 P; l2 f+ a% b! o2 Z
Z% U% w& L* T. f-f% V8 S0 m+ \+ V9 p
4 K) j8 p. ?( F. g1 R# Lconfig.conf* H' \! d6 y6 d3 f
# f4 C! m9 ?0 |. O-n
, A, d- q) y/ g/ s6 u# i Y( p K+ Z: T8 p4 @% V _, d
10
F2 w1 s7 R8 C7 t
- i. e! M+ R4 N4 Y7 }2 ~; K所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
0 Z/ _! t5 V' S+ C2 ]0 ]- e2 E) M% I, t! @$ E
“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:
1 Y# ^) I ?" u* F# y$ S
+ M( I* Z8 e. O+ G; v+ m* V0 x[vb]. i1 e% D4 K6 n3 H" A
#!/bin/bash* }' E( {% H, m0 e5 [ B+ ]
$ |1 Q1 ~. `) r5 T' J) o
#if [ -n "$1" ],参数不为空
0 V9 g& O, b* ^& A2 Z( G% r6 xif [ "$1" != "" ]
$ M9 J+ u4 s; I$ c7 Cthen
$ I" Q6 y9 `7 ]
! t$ h+ j" j& `#...有参数: j& q/ T! [9 G7 _- o/ K
o) u3 \- a5 T0 f& X6 z" S" T9 ^else
& m5 ]& `% K% u! f. m" F& Q6 A
( g/ e$ b7 \/ u) Ethen2 u+ V ^6 x3 |6 K
2 I& Z3 g- c+ I8 K, F8 e4 c+ X* x#...没有参数% g7 D1 e c; i
* f( q. O/ M7 J# i
fi
' G$ A8 P7 Z, U$ O
5 {6 p2 ~2 `6 d9 }, ^2 ^; q8 Z6 S: b手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。
* t' L% v& x8 p6 |1 c8 D: F$ L: v9 D: q8 H6 R4 Z6 ?
2. getopts/getopt
2 r$ _% a @9 L6 D9 x7 C" @, c( E' H4 \: q
处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:
! N! @& ^0 m9 J+ N! P9 R
. g( e+ W1 O9 d k./test.sh -a -b -c : 短选项,各选项不需参数1 T9 d, r2 Y+ x; V) `$ V
0 j, {' ^# _! h8 U& }1 h( @ I+ D
./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。/ w8 @9 r8 `( o
4 f7 c D1 d9 N( x./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。) q8 h& R. G5 ]6 V
# j9 W- h5 j* S$ I" x./test.sh --a-long=args --b-long :长选项6 l3 D% u6 O% T1 `
1 P5 b v' V* C我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh
1 I1 l- h8 V' c1 V E8 D
5 q4 _. v8 g) v8 Q3 A# r( Y[vb]# r. H7 ^ m! [2 V& d L
#!/bin/bash
& R9 `' C1 k. z, I& r2 s4 M" N& s1 W; y$ _
#选项后面的冒号表示该选项需要参数# ` g3 x2 w7 v% P5 ?! G) x
3 e8 k) b( ~. v3 s2 n0 D7 ?+ h
while getopts "a:bc" arg
9 F: Q" J! j2 Z0 \/ t
; c% y: q7 M) U: Fdo
1 [4 Z- F2 W+ r' S
2 `- ] v: z& M1 Gcase $arg in$ X4 J( W4 N$ f
. [6 a- P; U' v% G6 C/ _0 F
a)
0 e9 j! @" D. I+ K7 C
+ {) s$ Z4 E0 S& V#参数存在$OPTARG中8 U' s5 ~& r: Y1 Z( _0 n
( d0 E, N8 H2 z8 H. Cecho "a's arg:$OPTARG" ;;
. L4 q; ]8 M1 K; Q6 Y: C2 z: U$ c; h: B) f- s% `" S$ W8 S E t& Q# B( M
b)
& e& O% i/ l6 a# c9 ]& E
5 h2 d- }% G. Y- u2 Vecho "b" ;;9 |2 o0 {' j6 j
; ?4 h8 C- D: k" P+ s7 W7 O7 ~' b
c)/ _* d, m# G, W* C# }/ i
. v1 {1 m, [: |/ g1 a: V1 b
echo "c" ;;0 E& K$ b: L- L/ Y- q' d7 U: ^
4 T+ s6 J, N8 G2 ]1 d/ _?)
1 Z; M8 i9 a$ r4 |# K& _" n
6 g# D/ o! g- ^6 y; _+ [7 _- g#当有不认识的选项的时候arg为?
0 i# u( l1 \# S7 L5 \ G( D# D$ R2 p) @- @+ r- J' w7 d4 U* d
echo "unkonw argument" exit 1 ;;4 s9 n. p5 J8 |8 _/ t0 A
" P2 c$ q7 P7 w+ O% Y+ s
esac
+ ]! ^, u: W( u7 L5 j g: }& S8 X t M( O& }& W# ~, N
done
2 \1 j4 R2 b8 p+ @8 [$ M; z3 w' V* x2 W6 l; t
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
# h& H/ C- @( c; B O
& k) P$ L6 G7 H5 N3 I( f来加载了。
0 M7 x9 i, @7 b6 y- t; j
/ ^8 q' O6 P! Y) S% o应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
0 c }, Y6 g+ O0 C- A. a0 i/ ]. Q7 k0 Q4 C* N. }+ T
[vb]
$ n; K5 L5 c5 G6 g; [) `3 z t/ f6 J#!/bin/bash# X: t7 s, R; P* f! z3 S; ?6 y
5 X) Y* y! x3 X& \' O3 `
# A small example program for using the new getopt(1) program.
( w# {0 u0 r; [' M* n# e7 F* s1 @8 D% H
# This program will only work with bash(1)
+ N' E: p/ x( a3 P4 d5 z
+ |4 D1 s; k( R, T, v5 p1 Q# An similar program using the tcsh(1) script language can be found
/ D" j+ O _8 j/ n! |- h4 U! ?& C9 ^- K! `
# as parse.tcsh3 r4 W# U8 \8 O8 [6 | }
# V' q) d5 {" G, E2 P
# Example input and output (from the bash prompt):
2 S3 b2 T* ?* G
- a z+ D* ^- f r# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
D9 {" I% B7 J D. b* ^8 s( P* X! C" \8 C9 V( K/ ^1 S
# Option a, u* p% P" q& K. l$ ^/ O3 a# C
8 z, o: @1 d$ U" O# Option c, no argument
0 h* A) o: |) ]4 M
0 `, p3 F4 c" B: @# g# Option c, argument `more'- l% H" a5 C7 f$ w
: A# {! Y' e2 N# Y! O" k# Option b, argument ` very long '
?% Y \& {! j0 x7 I
5 D' T. e! U- u) P: G# G# Remaining arguments:: B# W5 p: y; r( G9 C
7 k* K6 o, K! |+ n" E
# --> `par1'& c% j E6 }- Z) ?3 G
& P3 Y l" N( ~ i; E
# --> `another arg'& H- @4 e1 p, L. B$ t
5 p3 V$ }: x' R8 i, Z
# --> `wow!*\?'
# p: g0 o- a J9 g! q7 l; B5 Q/ i
# Note that we use `"$@"' to let each command-line parameter expand to a9 `) H# ]4 ^5 U$ O6 z: ]8 l
; R7 X# W9 F# Z/ D' |" ^" j# separate word. The quotes around `$@' are essential!# a# q! u* D4 i( l- j# @6 I, q* v4 V
" C5 @$ [" @- u+ _$ x& Q
# We need TEMP as the `eval set --' would nuke the return value of getopt.
' t6 @' x: l: w- z8 j: o$ g) p/ E. @4 g! f
#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
) Q* h" ]/ a% i; N O; f1 k1 G/ H$ u
#如-carg 而不能是-c arg/ v% l& k! {/ h I) B9 Q0 ^
7 n4 ^( X/ F4 N* b+ Z#--long表示长选项* t, Q5 i( h& _& y% \
8 m8 |) e2 Y, j, m
#"$@"在上面解释过! D4 C$ ]* i/ f5 Y! [3 i
' S# |8 ?0 }+ J, v
# -n:出错时的信息7 V2 M: F5 o2 ]6 g# v# \6 |
9 X8 s$ u5 w5 @* N+ J ]5 A
# -- :举一个例子比较好理解:
5 l) ]( D* T+ d4 k- [2 d3 @
* q; K- _2 W+ y. |2 P# W#我们要创建一个名字为 "-f"的目录你会怎么办?
* f; `9 ^! Y+ E% ^, B; a6 A3 _) L# i' s7 X! K
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用1 U6 l7 u7 z ]5 y) G- K. W
" w$ _" v1 P8 z6 X3 c( q# mkdir -- -f 这样-f就不会被作为选项。
2 S, }% |: L8 ]5 M* D9 Y" u: G$ T4 K: [4 c& ?" @9 i" c( {
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
0 I# J0 h* G- q- d6 E9 y* X$ Q8 F! W
: L$ E, e! l" \-n 'example.bash' -- "$@"`+ t; [. z( N8 S( d4 w1 Z: ]
2 h: I+ z4 L/ v, B8 m
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
0 P7 V8 U; {: ^* O# Note the quotes around `$TEMP': they are essential!) `" \# [& m( c0 f2 j
; ?* @& r- E8 v1 k#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了) ^" E5 g( G9 i
( N& K* d6 P8 D
eval set -- "$TEMP"
, ]' n& n1 y A1 h0 G4 x+ G
2 y/ o6 c: i* N+ \" r6 q" S#经过getopt的处理,下面处理具体选项。4 P3 a5 n7 Q5 }9 E, E
8 M& w" N: H/ Kwhile true ; do
/ j. T& \$ X7 J: }" k: C! j+ J* `3 B3 n) s; V$ l8 u( H0 z% i. p! N
case "$1" in, V/ N" x, x9 @) z
- V) b7 T; j; D+ g9 p-a|--a-long) echo "Option a" ; shift ;;
5 E* V- o0 h" q3 q I$ N, j d. `+ ]" f2 D# a) y$ H
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
: x& W+ X' R6 H! \ E" S7 U, a6 b( c- Q/ S0 T& e6 Z B4 {3 y" B
-c|--c-long)- c0 A* K( T; Y- D$ Y
0 v/ r' m T# K. e3 \0 f I# c has an optional argument. As we are in quoted mode,. ~! R$ {; R0 c& p4 u# }
: G7 l3 W4 y/ x; R* n# an empty parameter will be generated if its optional* X0 {7 q6 e0 W2 A7 B5 c$ b
% Z( N S' Z8 u# argument is not found.
) O/ J- V# w# ^3 y5 j' a, ]6 ^+ _2 w* \! ^" @' J/ x
case "$2" in0 _+ i3 f. x2 @
0 n* Y. w5 `* l1 w"") echo "Option c, no argument"; shift 2 ;;5 I" W: C3 q$ b6 J: c
' y; @! k& v& W2 g' F G*) echo "Option c, argument \`$2'" ; shift 2 ;;. m. V; ?. t# @+ f
& C0 G: `# R% u3 A$ P+ a" y3 qesac ;;) }( T/ a2 n( A
/ `9 a% ]6 U* |" t) M
--) shift ; break ;;1 p* M# Y- ]" ^5 |8 m3 A
# L- j' S; k( g6 b) M*) echo "Internal error!" ; exit 1 ;;$ v) g4 h6 d: s( j/ A8 D: x
) ~" r( n9 i5 D8 Qesac! d, c! S% B3 F- c6 x7 \$ A
1 w! M1 E# g- p0 J* vdone
7 d. q- R2 W# ^4 Y+ V6 M# @! F( T* L I6 g) P8 V' E4 I6 R
echo "Remaining arguments:"& n# c% d0 G; j0 g: X5 u- k
4 N* s& y( R: s: U& T: H
for arg do+ t' W$ s' f7 x) H; b# z
4 U& s$ w# N: g( T6 \& z* U1 L5 a
echo '--> '"\`$arg'" ;
0 }) p9 w! y) J6 g" i( q4 e! t8 d! b8 x
done
& X& `1 v( g& _" S$ F. X X6 f
) f& T7 p6 Y( @9 ?9 h1 i
, \) i2 s# s. d9 z3 v; ^比如我们使用
1 |! `( P. Z+ O! M
7 `- u# Z! ]+ e8 D2 V8 l" [./test -a -b arg arg1 -c& w" U' a# ]- h0 C7 y7 ]
4 K, M* h" A1 m9 e, ?" Z! j
你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:/ o6 t1 `/ F- l. h" X8 A& h! @
, \. E: M) |2 `3 w6 ` U+ ]( ]-a -b arg -c -- arg1* g- {5 \: i; v: z
: ?4 e5 b& |6 S, {+ ?) d8 l, e/ }$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。
# E# ?! O5 q9 s9 D! B* R; V: @
$ ]6 a7 V$ i6 v5 K, `9 d+ y3. 总结
5 t. c6 D* V1 c: W n# ~3 v; L, m q* C; H: S
豹尾。: T ~5 b" F; `( f7 v
8 Y6 B1 j7 }0 G d! h
本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统4 Y& Y1 W" l/ X5 h( P
6 C3 b# @# J3 x$ O" d4 H, d |
|