版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
7 B, p6 l# b+ }% y! sgetopt/getopts:Bash中命令行选项/参数处理
& G9 o4 a& O+ F6 ]% m/ t: `. d( r4 U" V
0.引言
% F+ y; W, h( W/ R: W0 m. O. c2 k( p' X5 k3 _
写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
- Y2 o& W+ e1 m3 M
! i5 z! u5 x2 i" g* O$ R选项与参数:6 B% h; |6 {( g
- B. ~: s) D7 \, B" |如下一个命令行:2 S' t9 Q& ~7 c3 S) T% ~+ i J
' ^( M% y+ {& W# A[vb]! v' t& k2 f1 M* ]6 I; c
./test.sh -f config.conf -v --prefix=/home
# I7 O+ ^ E9 |. R! R: d! x6 ~* X: ^( y
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。
- t- L0 _3 N2 H8 Q- C! g3 K9 {
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。! {! } x: @ Z6 j& c, u+ Q% H3 A
! ?) ~- y% {' {3 Y* V在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。* d: n( p( ]! m0 X8 x4 R
) ?3 D6 @# M @7 ^$ b2 g. h手工处理方式
& W. u F N9 f% s0 S6 K% w: ~2 t# b
getopts
; S0 |- ?) R& k3 b1 r: @$ J- [. R+ u% a( {/ r! L
getopt
3 ^! z# b" v) O1 y; @2 M4 R
% b" Y) g; p! `* Z6 i下面我们依次讨论这三种处理方式。3 y4 h& |0 y# s( O+ X) U2 I1 l0 W
: L. K- ~! I7 b- h" x1. 手工处理方式3 N q4 j9 g. S# i5 f- Y
8 n" M9 d1 F! P
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:- U) v3 r; r+ P( {% b/ q! i: J$ l
8 A+ c. p! q5 q2 D8 k" C$ {$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]
. e) r5 I$ i) g5 {$1 : -f,第一个参数.3 c) C2 U- X. J. F
: z* _9 T# b d/ z8 A! y9 k2 R% k$2 : config.conf
* Q {' s6 e. d, \' p2 o5 O/ x8 L2 Q
# x: u, L- j$ h5 q+ h$3, $4 ... :类推。
6 I2 j& ]9 t* D5 D, W
( t8 S ^5 O/ p& ^- i3 h" P$ |6 j$# 参数的个数,不包括命令本身,上例中$#为4.* U5 I; V$ \" v0 Q5 J/ _8 Q+ V
; o$ a: _( s/ f* H+ l# Y( K6 b
$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home# `# H2 A3 Q5 Q$ v
+ `. q, N& S5 I# h$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:
0 ~7 \. Q5 L, c3 I" U$ @( E6 ]" Z% w+ x+ Y2 B1 {* {4 u2 m/ `- Z
[vb]
/ Z/ e1 a: Y7 i#!/bin/bash2 M: M: N' V; k$ E. E; B3 M0 G
" |: T* `5 A/ C- C- tfor arg in "$*"
! l4 @9 A3 f. c2 f3 ` q r, M6 Y- m) |; {, @- D- a
do) U* d" k( E4 W% h6 f
0 @( B* Z9 C$ `) P" J
echo $arg5 }. m! S+ \$ J) Q) p2 c
8 T3 d. S9 n' K4 i
done
. N, M. @3 S' z" p t
1 w% Q& c* {8 ]/ F+ A% k! r; S4 Tfor arg in "$@"
' Z8 _5 [% _$ B5 u
7 H9 t5 s% ^! l% W8 E6 x/ qdo
- e# A3 o1 g, |6 V1 |3 W/ n
$ R# w/ g2 w! F3 @( ]echo $arg
d) l# W6 Y$ O, l& S8 n
1 e& y( Q- A* Qdone
! k8 M0 \% S; m+ A) L. ?
4 z. y7 U/ ]7 t2 m+ B- u执行./test.sh -f config.conf -n 10 会打印:
9 G) V, t( t3 r. E; M2 |/ H
0 K5 T6 }2 R' y0 ]! d: W-f config.conf -n 10 #这是"$*"的输出
2 P( X$ T- r- `7 d/ H) r! U* ?# y' a! x% l5 Y z$ ~2 j7 f
#以下为$@的输出
% N r# ~, e9 ?* O! D
" C- J. c4 P, a% A-f
3 N8 G1 \8 S/ b& ^; S% l3 @) ^. ^8 y2 a4 L. q
config.conf% |5 o0 f) e4 [' f. O! v' `) ]7 H* B
' I3 Y! W1 d) R3 f3 y9 C* J-n6 X. O8 U6 z! w9 X' v
# g6 X8 o+ E" K; |2 L
10
; r- N3 N3 z" U' F' y' s) O% f; W9 ?% p
* b; p# [* ^3 M% e% q所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如' W9 V$ l" a2 ~+ i& J, r5 W
1 l9 e2 u% D9 r! d
“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:( R, T8 T+ ~8 m, r2 b+ e: P" W
2 `2 G) T/ A( t8 }
[vb] J, p. \( v$ Q& M5 U$ I+ ?- G
#!/bin/bash
4 a _( N9 b) F. a( c# C! Z1 j+ r4 y1 L! t! C
#if [ -n "$1" ],参数不为空
. e1 i( @5 ^ S. uif [ "$1" != "" ]
# ]8 L5 o7 q/ Sthen8 R, Q- q7 E$ x9 \0 T0 x5 Q; t1 Q
* F2 [8 Y0 g# H! z4 T o
#...有参数
& n h: L. q0 `( g# i3 Q& G; D! T" L
else
2 U- m; Q9 a' \/ p8 j$ D' f6 {
# ?+ I6 H q' mthen; ~; K- K" x2 I5 J! @
) ?* t/ c& [! {, a- f3 y8 Q& A
#...没有参数% a) z6 `# I7 T
% A2 q6 g* B8 p* kfi: P. B+ Q1 {$ K" {7 j- I* i9 s# P$ c
" J( I7 ~: [. u& E& w6 S* O手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。
( c- W5 Y' }- O" x( n7 N2 k+ `. A; A6 H6 C6 b ^* d( [7 s+ V
2. getopts/getopt
% ~/ H; T' C# U' h D; T1 ?/ F# G1 R0 Z4 x, P
处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:5 C) t4 k8 z) C9 ~1 N
% ?, _6 t7 f) c% C
./test.sh -a -b -c : 短选项,各选项不需参数
0 T# z, J1 x) c: \" F
. Z( x; x) l& h- K, s./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
8 T O/ o0 @4 [5 n) u3 ~! @4 v' g7 ~. d: ]/ l
./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。
8 x1 V! L& C% I: ^, O: s }. P- w9 E# d; M( {" l
./test.sh --a-long=args --b-long :长选项
7 \# [. q) e% R0 }' V3 I4 e$ {' S: x* F+ s8 s- e: c) F8 B
我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh n4 F' \" ~9 ]4 i( T% I
0 |% _. F: H) k& f
[vb]
. b+ B: E5 q, c4 m# W#!/bin/bash/ v4 N( K( {2 o! Q9 B
2 L6 Z' H" h' ? T( F& D
#选项后面的冒号表示该选项需要参数& p* J/ ]6 k3 C! Z" X7 Z& m0 }
) [! q! W6 G. C) u( S
while getopts "a:bc" arg
2 O3 L' |* s) g& {" T6 b6 |/ x' X7 M$ s T
do; E# {' w, p5 `" q- q- s, E1 M
+ S; Y1 c% n# D8 f
case $arg in
G: d" y7 Z6 h: ~ u
: d, B8 c0 U* y2 {2 Ja)3 @6 o/ ^5 E9 |- V1 s _- e
" E1 J& W$ Q n' L) ^
#参数存在$OPTARG中, `1 S& p1 c7 s- s/ z7 M
. T+ f( M5 S: Q3 E' [echo "a's arg:$OPTARG" ;;
$ U- S* x( _9 r8 a) m- d6 U* U( m4 T; G Y: e: s3 T' v' k# w
b)
3 Z) N# r- [9 G" W( f# {. r+ @% C! s: B0 t7 r
echo "b" ;;: s2 }5 S* d% \& |* v6 [
. E% C: B3 O* X$ F1 `c)
_) E7 _- a7 O5 r- X B8 W+ O) c5 g' }, Q" r5 y! r- \' L
echo "c" ;;. ^5 ]; f3 ?; M. A5 I
* ^& t3 U9 d0 v* u% c?)
2 K4 J$ u# L* n. E9 J z4 c" `7 k/ T1 [" i
#当有不认识的选项的时候arg为?
# y4 t! p" u# Y, U2 ^) }, e
3 @# v6 I& G: K' \5 W& V9 decho "unkonw argument" exit 1 ;;
6 J- k4 W' p6 E; Y( t7 X- W/ Y6 S& A Z' i. X
esac/ z7 z+ h) j# Y# g: G$ G! T
' K% y% E4 i# o1 \6 m Y0 odone
- O2 V6 m0 A' b& f. e2 M7 m" Y! Z, |1 D2 n
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
, L; f7 s! O4 A1 m2 V& S) g- [) c
; Q/ K; F# o! A2 G! A* v. Y) o来加载了。1 f+ b0 A! u7 ~( w! N: F
( f' {) j) k3 W' G' ]应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
1 Z3 R/ U3 |+ \) b' B! @$ @6 _% C& G/ i, j: x! ?
[vb]( p9 T8 z% N& d) h6 p9 [
#!/bin/bash
4 O- t4 Q% V5 p
8 G# f1 N0 {5 x m& e9 r3 G# A small example program for using the new getopt(1) program.0 c; v+ |+ V1 `: |
0 i8 o2 ?7 v2 ~ x" R; q
# This program will only work with bash(1)
( ~ I/ ]' T+ @" q
" @ ^' W5 `" b1 K; d# K! O# An similar program using the tcsh(1) script language can be found, n& U O$ l* g u% a: q4 x
" z g$ z" Y7 c* e# as parse.tcsh
) |9 L) R$ N2 |2 e0 H( B) o6 g- J. p z1 d4 u* H p" L0 Z1 R
# Example input and output (from the bash prompt):" e: V3 w, D1 B# k+ S% U% n
1 ~: q' p* g [/ l
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long ", P) m6 I3 D( g8 r
% @7 D/ P! Z$ ~# Option a
, X6 O- s6 Y( A( ^ T2 j# Y( m: h6 r6 T% e- E$ o {7 o
# Option c, no argument- b/ q4 h+ z [& O M
( ~- Q8 b* V1 U6 x1 e1 B7 l
# Option c, argument `more'! R2 t7 U% |/ \, |- _4 X1 j ?6 c
: y# f# W9 V' ~$ ?/ |, P
# Option b, argument ` very long '- m6 [! x* n9 N) b1 I) G
" h4 p. B0 S% c3 N
# Remaining arguments:
" p9 K2 y# n# n9 ~2 X+ q: S* ~8 R# U( {3 B( m) }# p6 ~
# --> `par1'
" J W( i: P2 l% Y& N" o( R+ X; Z8 V5 v5 z. d2 r
# --> `another arg'
) F* D5 ?0 Q2 M6 g0 d; o" M: J. }: k0 t- F$ p
# --> `wow!*\?'$ r8 \: ^2 N8 N6 @9 T& g
( ?6 O0 m: W0 n3 a7 Y) W# Note that we use `"$@"' to let each command-line parameter expand to a
; D, r7 C: M2 P6 j2 h1 }- g) I( V1 }4 ]4 u) J l4 X7 m
# separate word. The quotes around `$@' are essential!
# ?) K' A" ^4 T0 E
& i/ z6 m- I$ M( G# We need TEMP as the `eval set --' would nuke the return value of getopt.
" Q7 D( U1 R+ h: d2 {+ d" k1 v$ T5 g* |! S
#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项. A9 C$ r- Z& l: N" N# i6 F
8 w; X0 _# N$ D0 J) c- W#如-carg 而不能是-c arg
; K. e/ Y/ H7 l
1 R/ ?+ T* g& o2 y#--long表示长选项
, d/ G- a# p' v9 g# I; R# J) C
3 i" j9 Q# j- I. {, ?! M# s#"$@"在上面解释过
" U: b3 `) Y/ y, B4 r! I
7 v8 @2 A0 f9 _8 {( g# -n:出错时的信息 o, D: z; r& b: a' r) V
: T* { x* N) m7 {5 I
# -- :举一个例子比较好理解:; I/ E; T7 s) V1 ~. M d+ Z5 A
. {6 }3 i# }6 d$ I: w
#我们要创建一个名字为 "-f"的目录你会怎么办?+ p. w& z/ `5 S6 n2 P$ [
' i6 t# k h- \$ L" H- t1 j3 q/ d
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用& `1 D' |2 r4 [( h# O* b m/ b4 H S
! z. p$ r# ^% U q* J% Y" C: p' Y# mkdir -- -f 这样-f就不会被作为选项。4 Y- A4 e) s/ F# ]/ }
6 S, s: {8 V9 O8 e; t
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \; Z* J) h: i" I4 M
! Q1 G6 ^( }: ~- R-n 'example.bash' -- "$@"`
* y& o, q6 y, u- v' `# v( _" | M2 T/ M' S
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
2 | z y1 J, a# Note the quotes around `$TEMP': they are essential!* Q, v% V ]( o. |9 H
. O7 o! I# m: w- ^#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了+ G( s# Z7 ^! j; o+ E0 ]
$ M) P7 f9 E% H5 p
eval set -- "$TEMP"8 t4 A, |; y3 ~0 E/ m) O
( {* Q0 p) e: b#经过getopt的处理,下面处理具体选项。
0 u8 F6 A# Q2 f& X; P0 x% B
4 c/ v+ u6 y0 Q6 L* ~while true ; do
2 N2 ]) i8 c0 e, r9 Z; o
' L4 p# F% d# q3 X% Lcase "$1" in; \" ]1 J. H6 m! g, m- k7 k+ v+ n
2 O1 ^& t) R' E1 K e-a|--a-long) echo "Option a" ; shift ;;
7 ?. G& q" ]! K& x6 |. X
; _6 z- m) N2 `$ j3 A-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;% d6 f# S5 t# R% n1 W8 r6 N& T
$ a# r1 P K0 E0 D% T4 S
-c|--c-long)2 G( o8 J2 r8 Q/ P8 P! x
C0 x3 d6 O l+ K
# c has an optional argument. As we are in quoted mode,/ e7 `8 M+ R7 {: {2 u1 y; n$ B9 y& O
& O+ @$ c+ p" p# an empty parameter will be generated if its optional2 C% T3 ~% U$ V# d
( L7 W: X* }2 |) S# argument is not found.
+ P( p* _5 Z( j. t$ c' t0 v. n4 K1 k& W/ b' d9 q
case "$2" in" i& \) f% z0 w- A |2 I5 x, E
8 B" h+ M7 d, t0 j9 _2 ^9 k' P
"") echo "Option c, no argument"; shift 2 ;;& r0 B, z2 u- C- {/ C7 C
$ M! j8 y) b8 W" `8 u: t: Y9 \9 i
*) echo "Option c, argument \`$2'" ; shift 2 ;;# d/ j; ~% G! q8 h$ H3 K e8 E0 x
0 A5 c. w: K# d. G" a* ^esac ;;- J% H5 x+ ] ]5 N
2 a& t/ \* Z: f( Q+ D
--) shift ; break ;;/ f9 M0 B7 p3 d4 Z
$ Y! l0 u. c/ z% Z
*) echo "Internal error!" ; exit 1 ;;
* m$ p. U- F9 t7 W$ |8 H9 j5 F: }4 Y- \ ~, [
esac
9 ~! _8 Q5 Q! W& d" |6 f' S/ r
) F9 v& T% R0 Ddone
# T& u0 X$ v' I
' c7 X5 ~3 h1 r: Necho "Remaining arguments:"4 Q) T* O: C. m" e: P
2 |* i8 ~7 D# b. Tfor arg do* L0 [, M& L: ?+ ?
6 g0 M( B y- @ s0 xecho '--> '"\`$arg'" ;
3 l+ q$ b& S. A# b/ y; u! P" a
1 a/ h! @ x6 @% L9 f! t& kdone
% q# t6 T7 \) V. p$ ]$ [' Y, }+ p: Q) I( Q2 n0 T" Q. @
! v6 F3 ]' r9 T2 e N# x5 V
比如我们使用
( [: C; \/ B1 s1 {" E* }4 ^4 [1 W# S/ |0 f2 o d" X
./test -a -b arg arg1 -c
7 S0 L0 R+ j, k1 Q& W! S
" M) z0 O0 b, h; J$ X6 D你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
; h1 |! k: d1 h, W. U* s: F* I t. J+ n0 ]; h9 H- V+ e9 r
-a -b arg -c -- arg1
! X* ]% m6 M$ Z2 Y {5 M- x0 k' B5 r( E
$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。( a; }# S2 C* p
% a3 a( J' V+ T% Z$ @
3. 总结8 g7 Z/ A: `6 b' t
4 v. ^ \) k: R2 t, z. D' q
豹尾。2 s: r: h7 W! T* Y
q& c4 ^( S8 c0 C6 \$ [; n
本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统7 ~7 @( H' ~7 F8 f6 r
2 b4 G0 T5 Z$ C. U
|
|