版主
主题
帖子
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
5 J! Q9 R9 l: f+ dgetopt/getopts:Bash中命令行选项/参数处理: X0 I3 }$ D1 L0 N/ F7 V) X
% Z8 P2 N, _! r
0.引言
8 D$ g1 I! T* M: A" H& {
' N% [0 G. Z: }( f: K( d& E写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
" c7 Z% [ y! m) `) F! R/ v/ Y$ [7 z0 ^0 r
选项与参数:! a: ^' [: q2 U: h3 v
* u$ h: ]5 [$ K& [7 t如下一个命令行:' p, P. \3 |8 Z2 o: E7 O
0 n* M+ e3 _% _6 H* P9 [# Y[vb]
* g8 v6 G7 y, m7 u./test.sh -f config.conf -v --prefix=/home* o' ]7 X, C0 [0 b& E: E2 B9 b
" F5 J3 {6 x/ }! b7 |我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。7 A9 G3 h3 ?; k- t, t
$ v. Q& a, K9 P( j7 ^; }
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。4 o" W$ y# C z: D. B" L9 x
4 {% ~# W+ y) j- Z; P# V在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。
4 R! c ]1 u% a
1 p+ }0 m |, f3 S3 o! _) L4 Q9 I6 Y手工处理方式+ `8 U/ ? _6 F. e. L2 n8 t
! Y4 J4 J1 Q2 c3 ^' r
getopts
9 w" d0 ^/ Y' u! o: b
) R5 M$ Y" v2 X) pgetopt
" M8 |$ o7 r8 z
8 i( d4 x" R! e; K0 Y3 o下面我们依次讨论这三种处理方式。
8 y% v- M) W2 s; @: ?% k) G. q7 d$ I! ^4 ~1 N" @3 `
1. 手工处理方式- n3 g/ d% f8 C: a$ u2 F
1 d) M# {( H c! W* F/ X: P
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:- A& A, t$ d* n0 E3 z' p
1 r* b& ~4 p( p
$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]4 Q9 s' ?: K( a
$1 : -f,第一个参数.
. I: Q+ c! z. S7 S% z4 R
3 S# p: q. C* O. }! q$2 : config.conf+ x( c! t6 W( S0 |6 R" V/ s
, V0 y' r( c( u$ ~' Q: A T
$3, $4 ... :类推。" E( L2 H# n, P1 ^% K
* y$ J6 S* a! ]6 ~5 r$# 参数的个数,不包括命令本身,上例中$#为4./ n. ~, o7 r0 b
2 O* V Z' S1 z5 D% p$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home
0 ~. }# `4 N7 T! D/ {9 k n& U' O7 z# D' I8 r: v: e
$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:
S; H* v0 K4 f2 Z' h
1 [( B' }1 i5 y" e) l[vb]
0 c# e$ N. F0 h' C6 }+ m# ^#!/bin/bash
" l6 {% y# P, Z4 t0 T* M$ q* q0 J' L& s0 \) B
for arg in "$*"
. Q4 o2 F" R2 ^3 K& V8 X' x' H |3 o1 Q+ A6 h* h0 T
do
6 `1 r5 U i9 ?+ b: r) U/ S
7 }) G& h, `5 a- ]: F8 g" Cecho $arg
0 P' G3 W: a. w* o5 `0 _' p
& x W' b P; N9 V1 J5 i( mdone. R1 f8 Q( ?8 f' @' f) _: x
. ]1 i+ _* O: b% X( J) C5 w6 Y
for arg in "$@"
' x+ p- B, e3 ^: ~3 P% \* z, p8 O' ]( U
do
# ?4 v0 K: M# ?7 G& g& |( I9 K9 d# h) ~% d* I* p
echo $arg- k2 w- r% E N* Q
, U, S8 Y1 {6 X) t
done
7 l- z6 I G! A6 R4 X7 A$ t
# w( s: T4 c1 ^2 ? r执行./test.sh -f config.conf -n 10 会打印:
2 a, L% K7 x; i. [$ ] ?2 N8 w$ X4 A& ], t4 v0 v3 R
-f config.conf -n 10 #这是"$*"的输出
4 A) _# n5 F' V# _ X& V$ H
* ?* T0 L* k- V0 i' E' y6 j& \#以下为$@的输出& k) ^1 w# q! |: ?2 o6 R8 A
( j3 y9 O+ m8 G( \-f
3 s; k( [6 L. p. }7 H y' R9 T! p3 A) @2 q0 A$ P5 ~
config.conf
" r+ V$ d {! S, f7 _$ E$ G2 N; ^" r4 Z7 Q- U) `' ~" q/ Z1 b
-n! `- n# P ]6 |( m' ]4 X& J
: m" |2 b J3 i* ?4 \' C! ?' S10
, _# x& ?: X- L( }7 E
9 |& P; h' P! b6 i, |0 W$ p* J所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
$ P: g1 O9 w& I/ j
) X9 X, z0 l0 [( c; ?+ g b) H“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:
( y2 F7 Q& G- J/ @- l& o$ y/ V$ I: l- ~+ q
[vb]5 s0 B; t6 n+ V! A* \8 ^# M& v
#!/bin/bash" ] F+ u2 s" R i
# @- i p. x2 C# p. m! B
#if [ -n "$1" ],参数不为空
, k3 W9 Q+ t2 Z- ~if [ "$1" != "" ]
5 y* i7 d& w' U1 L! pthen
0 n8 K9 [! Q1 U4 h7 p0 T$ t2 Q# C2 i2 ? C% m
#...有参数, K) w* H! a. U0 C2 L
. t) S! h; p% P8 R, Qelse
. m8 _& f9 L" o, c
6 f' o& V7 ~, V ^$ D4 qthen% ~0 u$ m( { x- o# @' [
' T* o* v! o: y! p" Q2 q, Y3 R& I! j
#...没有参数% D c" w' p2 I- ]" ^6 L$ U: T
; B5 _- _& f) U; n% |" s% U
fi2 e8 E- A( [( i. e
% D& A0 r& I) i7 x手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。
8 [' H( _7 q5 u( t% K- K" B% S; y$ X: E6 c5 U7 y3 w
2. getopts/getopt
. v6 ]) r7 o, Z) k2 @2 T1 F' z
8 @0 S- j0 h, C; E处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:) Z: \- J7 E2 B" F- C, ?3 W0 ?6 q
* E2 F$ R3 s ~4 P) N./test.sh -a -b -c : 短选项,各选项不需参数) c4 F6 I, f. x8 K' e
& @* m/ `0 o: ~./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
# j4 s/ k$ k5 s8 ~4 W, U% i
' R' X/ w' W+ G6 N./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。
+ w2 [: g2 c F N! j( w: [- ^; G u1 \9 z% V6 f4 G) R5 h
./test.sh --a-long=args --b-long :长选项7 e# H. f. i ^6 e/ h
7 C) t& T" _: Q我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh/ g( _/ u, w- }* a. ?
2 Z9 ?; Q& x$ W2 d* v* A
[vb]
$ b6 m% Q/ @0 L3 N, R#!/bin/bash# \% O* |# f2 M1 W
( M8 v3 h* t% r+ ~ A
#选项后面的冒号表示该选项需要参数8 F( w( B/ V9 g
% G. U) V% ?; v) l7 Z" ?6 B6 `while getopts "a:bc" arg
# o; P/ O9 A1 c& s/ c8 {, G5 k! s, X+ b
do
* \/ h- e* C: v9 C
1 m! n% K7 k3 zcase $arg in: C; j3 x, C' g! H7 F
6 X) n: S n; Ta)8 `/ ~* B8 I; T' {( l7 Q
- \. C+ V" m7 ~9 h6 {7 y
#参数存在$OPTARG中
/ W/ D: n6 v4 s; [
3 x. R* u! D$ w) y0 Z3 |echo "a's arg:$OPTARG" ;;3 n1 b. C# e( b+ \! p
& H% ], m; c* P. }5 ]9 ~
b)
! w1 K. f; N4 {6 p# Z" U0 P% B) Y2 E8 a
echo "b" ;;1 v' v8 L7 ]* S' _7 E6 d
! M0 { k; K$ w; b1 E' ~
c)
7 U, Q) \# h, G) A1 I2 L
- _ @" A# z0 G1 [7 z) Decho "c" ;;0 }# Y" `4 Y/ Z" Y9 D1 `- g" F8 B
$ s9 L3 \ q& a! w; v?)
+ C9 W. X* F g# a3 _9 z M5 c1 E. ]( u, x0 U4 J. e6 E; Y/ r
#当有不认识的选项的时候arg为?* L% b! O) A1 E
$ p7 w% }' C5 k0 P& y% techo "unkonw argument" exit 1 ;;
$ d: w. t3 J& a" b5 r- j
. X& G1 i4 {, J7 Uesac j7 ?/ W4 _/ _
0 x( h4 r5 N/ n& Q7 n& bdone
% Q; g- _0 T) ]1 r. D9 V
: u( L# U. l4 L现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
* b1 A* d& u8 B
7 e# Q3 M7 I" f0 D2 y来加载了。: }7 ~- u* [/ L4 N( t# u" H
/ X6 y& F. T! w- K" e
应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
5 [. u# n" c0 k- l E; Q) U9 U* p, u
[vb]3 M* D/ ~2 P% h- Q5 W8 P
#!/bin/bash& z; Z% `5 O9 D% r- b
N& R. m* l; [8 S* Y4 E$ y
# A small example program for using the new getopt(1) program.
: \* i6 I5 \8 S. w
: C: q2 {/ y+ S/ p3 N+ @) ?' z# This program will only work with bash(1)
) e( G/ J: A& {- A( D- D0 a8 [& M: x# ^: U9 ^: L
# An similar program using the tcsh(1) script language can be found6 @$ _: k0 w8 t; \* w
' M( }2 B" n; X) Y" u
# as parse.tcsh
+ h9 ]5 j' N- \; O$ Q( i: ^6 X9 ~* v; R D9 I, y$ q L
# Example input and output (from the bash prompt):3 W1 f6 a! a1 Y5 ~2 B
5 _5 }+ ~ k8 u+ X6 y& W# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
) x! L& J' `# L2 ^3 K
) y! A; u! \- `4 m7 A1 g) m, {; X# Option a- [ i' f+ K$ b% ^# M) ^
! O& }2 p5 H3 f
# Option c, no argument" T' ?- U" M2 n# E: F: T! u7 N7 g
3 @: F& o9 r- K! `# Option c, argument `more'
# n. ~& a) V, @% R! y4 `' Q/ Z. I6 G, j" Y
# Option b, argument ` very long '
- R# \+ B) o& b
1 c% Y/ x% i1 q9 |6 O3 Q0 r# Remaining arguments:
T; }8 _& Z- t3 h' s+ M& Y3 Q
" V. z: |7 Z m# --> `par1'9 p% O/ t% J9 l
u7 x9 S5 T: `
# --> `another arg'
) D+ H' r( X- V- A9 k' e# q/ H9 F: q; J1 k
# --> `wow!*\?'
5 N5 l" `$ Y' T s ~4 ^8 K/ `& `0 E: w
# Note that we use `"$@"' to let each command-line parameter expand to a5 ]1 T) m! G& I1 q$ S
6 l0 e9 h# g- q. k+ B8 Z3 K3 }; e# separate word. The quotes around `$@' are essential!& I) l6 C+ T0 u$ E) N- K
, F( k7 R3 ]7 |1 `1 q0 u, G# We need TEMP as the `eval set --' would nuke the return value of getopt." \4 y: X: _5 D u9 D7 }& N g) T$ U
- M* Z9 J; d0 Q3 W! S#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项 x* f U9 w9 _* ]0 p
8 \2 B- \" r/ |$ ^+ V' U, l9 o
#如-carg 而不能是-c arg! Q* y3 W7 N1 f4 C' }: X9 [
$ {/ L4 A8 W; ]1 a) B
#--long表示长选项
. ^ }1 m' D% y' r8 W
$ {% a- p7 N9 c/ m0 o#"$@"在上面解释过3 Z) p: _4 j* Y" U9 {
- U; `- g% D5 q4 Y& H. I/ g4 P
# -n:出错时的信息
/ n% O# R1 o- v* U9 E. b8 b% }7 I. u4 M2 D1 f' Z3 J. Q
# -- :举一个例子比较好理解:
7 f2 S" ~( W3 S7 d( ~0 {! n2 H* {2 X' s* {6 [
#我们要创建一个名字为 "-f"的目录你会怎么办?; ]1 V% m: _! v8 W/ I
" i: M4 W; I- ^
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用* A; `( A- `1 f+ h
$ U& x' a- |6 E7 q/ L, |* [# mkdir -- -f 这样-f就不会被作为选项。% n" N/ E: M6 T# R! R! G
, v9 E3 ^# ]2 l4 s/ BTEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \/ a4 F- g/ J% F8 h" w% M- b
. M. H$ i( O6 G: z9 h9 i/ ]6 _-n 'example.bash' -- "$@"`* h0 \5 s O! j. Y
1 Y: k: ~& I. e$ b4 c- v# qif [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi , G$ n" }4 L9 Y- z
# Note the quotes around `$TEMP': they are essential!3 ?" q* Q/ p3 Q) D5 v9 c3 m' ^
4 ?! w0 X2 u2 a# A' p& s% n* R
#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了8 x# a& O+ w# `) e
: Y9 J) k6 \ _
eval set -- "$TEMP"
8 p F1 s% w) x8 [* W7 ^1 x$ |0 Q, a: @
#经过getopt的处理,下面处理具体选项。
3 @( c+ E$ u4 z1 q7 Q- s' X ^5 }( ]* G0 `7 e" m0 P
while true ; do5 P W* | o+ E! }2 J: L5 X2 }
1 q! A& L, [7 W4 F+ d) X0 i8 D: H8 E
case "$1" in$ S4 j/ H0 G( b4 @' [1 L4 Y
8 w5 a1 |# K8 ^) \" T4 X& r-a|--a-long) echo "Option a" ; shift ;;6 s, c' j1 h; m
/ c6 `: g5 g2 {, w+ v1 d! {
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;9 {* m5 v' z. |% A3 C2 [
1 x3 h h/ ]0 h" Y( l% P9 B-c|--c-long)
' G$ u7 {! ~# t+ ^) V7 j! d; n8 o4 C. Y1 ?: i$ R5 I+ Y. r( @% s
# c has an optional argument. As we are in quoted mode,
* N4 ?. S" \% e& z) Z! }
a4 a6 }! R# K% g J! d# an empty parameter will be generated if its optional8 m# v) J" a/ i
) {& _+ H( k0 Y5 o5 o. `7 j. A" \/ ]/ i
# argument is not found., p. K8 g U' |4 B: J1 `- w
# W# M" a i/ k! R/ K4 x
case "$2" in; w1 }3 B% r4 _! N- J7 F
8 u" F& j, F; z: `5 Y
"") echo "Option c, no argument"; shift 2 ;;
F1 {, U9 R: M: v1 v( Z6 x) x- S0 v [# d9 Y2 _) l
*) echo "Option c, argument \`$2'" ; shift 2 ;;9 A" E5 Y$ `( W% |& P
2 T$ K% F9 g% S( o2 W; S9 x$ Yesac ;;0 Q+ m2 N& t* i' ]/ g0 R
' @$ t- S1 t6 |7 t- F& k6 l
--) shift ; break ;;- `; Q, z; k" c- V, X/ N
5 U! w% ^- ` {/ i' c*) echo "Internal error!" ; exit 1 ;;1 v. r; f/ u, a( e6 h, L4 f
' X: n* l9 k. f }9 o4 s' y
esac& v+ g/ {! {& A. N1 T' {) ^
- Z& ^% }& |; Y# Zdone1 r$ z9 i K5 \- i) Y' P% c1 X1 B
5 C1 D* u7 v5 F2 L5 O# V: V
echo "Remaining arguments:"& t/ v4 x L3 b! H# a( a- @3 P
' ^; Y0 |2 V8 N* T
for arg do
7 p6 g- e9 y# Y7 i; B: y# k9 j! E/ g( U) f
3 ?* j, O4 `; t0 t! V* Z* f* `echo '--> '"\`$arg'" ;/ L, b6 O7 A! |: S! n8 k- h8 G
5 J' B1 J) q$ l
done
, q1 O& U2 U8 S; T; a* i+ \
0 S: I7 ?; C; y0 e4 f, O& T- z ^" c4 q
比如我们使用* @$ Q0 A. u0 @: o
' b0 o( ~; H- G) M; C7 H
./test -a -b arg arg1 -c, P0 g! Y8 `- _5 I- y
0 B. M4 N4 [) P# |5 m1 J你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
z' Z" Y2 q+ t: v+ W ^4 Q- A5 U: m. m+ O+ e" N! E& w
-a -b arg -c -- arg1
' ^& y; G" e- W, L6 b- J; i. Z4 \: `5 h$ V7 [
$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。
) L" |3 m, S; M, X Z" g8 N7 E O8 p) t0 n0 g
3. 总结
6 J7 Y! r( @7 D8 ^! k
& A. p5 x8 d3 R' R2 e& L4 P/ I2 ]" d豹尾。; W/ {. E$ l% y+ s+ J9 E
' j* F$ \4 w5 ^' K+ ^2 |2 `( C0 P
本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统! P/ S( p" N% \3 G! {
* X1 C! L7 J9 L- v
|
|