版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
. V6 ]6 E, _7 |8 N! ~
getopt/getopts:Bash中命令行选项/参数处理" b4 m% ~. v3 l
$ v& I, t% D3 \5 E
0.引言
6 Q' T" N5 q0 E
0 G7 E# C$ t ?. c+ V写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。: T7 e* |* h. O) K
h! a: D# Z) V& J8 ^& w
选项与参数: A( s9 |7 I# X$ A# L
% Y0 L8 z2 e6 o _' |) O. _5 E) s如下一个命令行:, \1 h* _' T) a( p
' T, V: j3 d2 P4 F8 B
[vb], Q& l* L& m$ i' b
./test.sh -f config.conf -v --prefix=/home9 o& z7 I" T8 w u. q" D7 F
: O# D6 X0 U4 A; M3 p) k1 J# u0 ^& ~
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。
4 }# k; o. ~9 \! z- \& Y
+ D4 V+ l$ ?4 h2 [--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。
, h) u R$ J6 r# }7 o/ Q. V, J! d
8 ~3 }7 a& ~% y& B; @% s( r' d; J在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。7 A. |$ \* S) Z, W& D3 N" B. ^ ^
6 u" x2 ?3 r7 w1 e) \) Q( _
手工处理方式+ n; \( c3 c1 x9 S( Y o
1 Z. W9 {0 O# C5 _) \7 N9 f& l+ P
getopts
6 u* m* O( M4 j( X3 f/ b1 r6 S f% I5 p5 O" U
getopt
1 U0 P7 j1 R: S. Z: ^% P) A2 \5 s/ ]- z5 } I
下面我们依次讨论这三种处理方式。
3 h& h9 K1 o# [& ^: l0 S9 P+ V3 z* B, w
1. 手工处理方式
' a0 z6 ^& q" \1 Y- r8 A: B% c5 |8 W6 u! I8 g% D" s3 c
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:! M% x! z6 `; }* j4 P
% W: K8 l/ @- _3 h8 w
$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]; O" m% |! A" ~# z. \
$1 : -f,第一个参数.8 ?5 F4 I: W6 i) A M# H
" M. O) w( {& o; o$ W! w- }- c# n, X
$2 : config.conf) |5 K: u1 ]3 `- d, }2 C4 f
7 I6 L6 M0 n& G. l0 ?+ ~$3, $4 ... :类推。
* L3 J0 K0 i4 F$ x2 j
+ j# L4 H- s5 H. H- X7 E$# 参数的个数,不包括命令本身,上例中$#为4.1 H+ j* w5 }1 G: ?
" B! D9 r/ h$ p- n# _
$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home& d# U' \# h( f
2 I1 C; m7 d$ o3 l3 q$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:: a2 n' p, p* r j
, ^! M: R. t! e4 x+ S
[vb]2 p* D2 a" Z8 f |
#!/bin/bash
# r8 R! s8 A/ f2 s/ z0 R$ x" b4 F0 M$ E {- c7 q/ Q
for arg in "$*"
" v! U0 y7 m0 `8 x" }0 B8 J. \; b% H( d( k' g5 T: y8 |" W0 H
do
0 j) h$ R" o8 a1 [" o5 Z
! h& z" [) @8 S" f1 i" Cecho $arg
4 U7 g9 m8 N# P* L; S; A# C3 g1 L% E7 B! F7 c) j( J' u, Z
done. J& x. O. l. V6 d- s
' C4 V+ v- @( g+ {6 m
for arg in "$@"
$ x5 R* M- F3 R
% Y P. ~9 i: I& e6 K" Tdo2 B0 m* I7 m5 g. d9 @% S
0 a( C6 ?7 ^6 _6 A2 D/ n5 q5 zecho $arg/ L8 R8 G" l' ?+ `+ d
2 J/ a2 q( M5 {* W2 x# {
done% _' |8 u$ y& y# \7 u6 k
; `* S5 a' d9 T O" s; @& s1 Y6 D7 |3 [
执行./test.sh -f config.conf -n 10 会打印:
]5 N! h6 ^4 g3 m' n$ {
6 }. k& k6 J. f: \( Z: L-f config.conf -n 10 #这是"$*"的输出- ` [( t! h+ O0 S4 a1 m
/ X4 U1 {) ]. B- Y9 `+ D# `9 V#以下为$@的输出
. k. V$ \5 l* W9 @ K' P7 B* T4 s
-f7 m7 J. V# A: [. C b
5 d) F, A+ C1 K" E+ m6 k( E) oconfig.conf
# Z8 e. r+ @* ~( O1 M* @$ b+ y% k; k7 p
-n
4 g& T; [1 D; g# p
2 t! t0 D% T+ m, B3 A/ _10% {. g( L) k [6 V
! y o' h2 R0 }
所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
2 k2 g" m" q2 F8 W0 L) @, `
0 D1 Y9 W2 w0 D! k* m5 k“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:+ h& z" r3 c$ n; N5 s
0 `- J! ^7 k+ f" b/ }# R- I4 @$ Y
[vb]
3 Q7 w" p) i1 k8 m- t; d+ a#!/bin/bash
8 N, O' x7 Y' r) z& j6 `4 R/ M3 T8 F+ d8 H# [( ?( _) x
#if [ -n "$1" ],参数不为空 . Z! V5 |* a9 K+ y! U5 ?$ A+ {
if [ "$1" != "" ]
! |0 J- E5 P( a' N8 X6 [then% `% j0 k6 B. N: N. p
a) G- L m! h/ _/ E7 H7 v
#...有参数- n* R% A- E- ~ f- @8 t' a" d
* j) h9 m. W7 e8 _0 X% Zelse
1 F! p' Z$ D. a0 p5 R+ D, O4 H/ E9 ~0 u9 S; Q0 r6 Z- U: u
then
1 e F$ R6 v; F5 [) t. e0 p% ?5 o. _4 f, j* l
#...没有参数
% {/ Z0 `& @9 J% ?
* Y7 S* l! K% R# mfi
* @ b, n, E0 G0 r+ z! [8 x% U8 K
手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。1 L$ H9 R) i7 M% k! L
( v0 B) q3 q, f6 K8 z$ F
2. getopts/getopt
2 |/ k( N7 f. O5 V& @# r; j8 g% T& e1 `
处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:' ]9 k. k3 P: n
# H# v) t) X- k' F* Y$ N
./test.sh -a -b -c : 短选项,各选项不需参数
! k1 n% n2 r/ I9 Z& `
; D# u5 V) h, p8 E./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。; y: D+ | s( b9 T: q6 O1 l$ m& |6 V+ z
" q! W& R7 \6 K$ c' q( k" }6 M, ^" t
./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。1 o" j) h( Z. \
0 ?) B; M2 J# h* \) {+ e+ J9 V5 K./test.sh --a-long=args --b-long :长选项
" _ ~0 q( l' k+ M. z( U R. }: C4 r' Q2 t" j6 _, h1 F# }
我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh
* t- v C6 L* F/ ]! x6 y' N7 Z- b9 w+ K6 O% v2 J+ a# Y6 F
[vb]" m/ Z3 F9 [) ^2 d
#!/bin/bash
7 p3 ~! a8 \, P: K/ o) L; G+ N' Q
! Q+ U- U- E$ Y+ q. X4 F* L#选项后面的冒号表示该选项需要参数
+ W" o8 o. E1 R$ ?+ P
4 Q/ h$ r$ } G1 h% |0 Lwhile getopts "a:bc" arg
! @* S2 q5 U) a9 W0 Z% W" N& S7 Y! j- m/ G% i5 ~4 P4 E
do# m+ d5 S% q! [. w `
9 y. `# r- L. w; ncase $arg in7 `( X$ n6 g( w; K
) C. L6 }9 ] s7 P# y: @
a)
5 J0 I- x0 Z1 z C r/ w& }
; f; T1 h' X, d+ Z#参数存在$OPTARG中
# F# F! V+ }, `
. @$ `0 v$ c# @; N" m6 kecho "a's arg:$OPTARG" ;;3 A+ `' l" o2 I Z- j
\% e" n+ {# C J
b)8 r3 C8 e8 ]- h
_: {# [ X1 F
echo "b" ;;% f. z! d' m3 S$ M
7 ]" O. K. l( m% {7 r2 z
c)" K9 I# i+ A# e- i$ _1 ~
4 @9 A# b, ^' n+ Y
echo "c" ;;% x6 `, F1 r* z6 Y* Z1 r4 `
, N0 @4 I; a3 R& F; M7 X?)9 @ l2 G9 F. b! M6 G- @ J6 i
& M) t5 P; y& A1 E& F0 E& \#当有不认识的选项的时候arg为?8 O$ ]2 Z- C- h' L$ r
! Q' ]! M; S% Q5 X: L/ o3 S
echo "unkonw argument" exit 1 ;;
9 ^' X n! E3 I9 p% N; r% z) x- r
6 g; U% l) H: W# o: s' f, g: nesac
; R* w( r, S. B$ Y. x, |5 Z# I. Q- i5 H! \- H* @. o, ]/ f7 O3 G
done
, ^% c! B6 x g6 i9 t+ A$ x- M* W; ^- M- Q# O% G) X' g) h
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
! {, O8 Z3 o. k: z/ ?2 y) `) I$ c6 j7 A6 v0 z
来加载了。2 R( f( r: n+ A
; p* ]; d# f2 i+ p: i
应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
+ U8 w ^& `' O' W$ r7 t. b: H$ }6 f5 j0 D! B
[vb]. L- a8 C4 \& f* T' k
#!/bin/bash
% W9 P( f; j4 H# |) g
4 l# @5 m/ S s( X: ^4 C9 F# A small example program for using the new getopt(1) program.
) c; F- }' @! ^4 G* z
/ c7 F! s! }1 N$ M# This program will only work with bash(1)& e' e1 r& U6 b8 b! P# c" ^
& |/ m1 Q0 ? w5 u+ m ]2 F0 O# An similar program using the tcsh(1) script language can be found6 N/ g8 b# ?$ S0 A
7 U$ b+ t) F& F" i9 j
# as parse.tcsh
$ ]& {$ N- q: q! l$ G
3 R" r" j# R- B0 z7 t; |2 i" L+ L# Example input and output (from the bash prompt):
; m) x" D9 ]% S3 c4 d1 P# M0 L4 b% z: B, V
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
4 p! @: R0 W+ j- J8 q! }: A" t( b$ |3 X9 ~* f* z3 A
# Option a1 m4 _4 L+ Y% h' _) v0 G) Y) l
; W1 Q( t& @9 L6 w) p2 v
# Option c, no argument
7 u2 X0 t8 R U/ Z2 y9 j5 j7 z8 h& i+ h) R) g; f- q
# Option c, argument `more': S2 @5 V) {' d( v% s: ^4 z
9 Z# p& K0 Y: l9 C; d: ^ p
# Option b, argument ` very long '3 A# F9 i* c$ Z$ j8 h5 ~$ p
7 B: Q$ ^- x% y! X) Q
# Remaining arguments:8 Z' j3 \2 m8 V9 w6 T0 C
/ J" {' E2 _1 b
# --> `par1'
( B5 p, B" {2 y* t8 p# P
2 k5 |; `" B" U7 d( I- ?6 {4 R, W6 K# --> `another arg'2 Z: a, x4 x4 y" m- R
3 E; i6 H. C+ }1 }
# --> `wow!*\?'& j7 p- ?1 o1 f3 U- b
5 E& m1 W7 D! E/ k; t# Note that we use `"$@"' to let each command-line parameter expand to a
+ g& i! u: d5 i; T. C- B& M2 _9 c- ~2 N* z R) _. ]6 _$ d- L, ]
# separate word. The quotes around `$@' are essential!
1 q" e5 K1 M& t* U" X1 Y3 ? N" f7 _$ S
# We need TEMP as the `eval set --' would nuke the return value of getopt.
9 v. ?" d& @) h! i7 m8 @
) o9 `& y- i& K9 S. t. m6 }. p#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
! M' j7 q8 i! T
0 F v! A: n4 Z( M, R#如-carg 而不能是-c arg
/ m+ X$ k x+ p/ [( |4 g( ]
. ?' e5 B' P% S. R- [& ]9 n8 p#--long表示长选项
4 @' W& Q0 o v+ Q; h" H0 x/ e' |, R9 j: r% d
#"$@"在上面解释过
! M, `3 y+ w6 h/ S
- [8 b# a/ b/ D' p# -n:出错时的信息5 N' X8 q0 J' {4 T! @
2 W0 @5 J& q1 }1 w! s
# -- :举一个例子比较好理解:) q. ]; }$ u% e2 v
# D/ l6 ^: b. q2 d#我们要创建一个名字为 "-f"的目录你会怎么办?9 b5 b6 P; F+ u* x5 ^4 B7 D
0 g9 F: }, J5 U( v0 g$ N/ M
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用
! L) E$ M/ ?6 G9 @( a0 D1 ^
# M- X; ^$ y- M" Y# X# mkdir -- -f 这样-f就不会被作为选项。. I* i/ e* Q B o: n8 }( g% G
" P( F( }4 `3 J+ x1 MTEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
- g/ E* V$ t# y. L" a5 a$ e" u
7 N$ D8 w/ t2 D1 W-n 'example.bash' -- "$@"`5 }: Z9 s% \8 C, g
, @ A/ N! ] b" V0 O9 i; |
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
9 p. m4 G: o: f& f2 A& H# Note the quotes around `$TEMP': they are essential!! u; ~; T+ y, |$ \
1 S! A) O, g8 a( ]2 \; ?5 E% k
#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了* Y4 ?8 q$ E0 e$ ?7 F. i
" O7 P0 y+ A9 I. Z: [
eval set -- "$TEMP"- ?0 Y, d6 Z( j
8 h. D- G0 t! P2 Q$ H3 C8 ~
#经过getopt的处理,下面处理具体选项。7 @8 N: J8 A- V5 \1 {; l9 e
/ j# J$ o8 m0 bwhile true ; do1 ]5 b! B, j5 D! q; E, L
9 m, Q/ l/ D8 b" `$ u4 _5 ?case "$1" in
9 F# L8 j( \8 R+ s; g) A2 G$ s! s r; L7 N7 y
-a|--a-long) echo "Option a" ; shift ;;
9 p; R9 q- ?+ U+ N* H" A% k: X* c& E* P/ v$ \$ p E6 T9 s
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
0 ~, u3 q. n5 V8 R. |. I, W: v. B4 H/ A* L
-c|--c-long)6 _7 p& |6 c$ R# j. `* V" P) o: A
5 m+ C& g" O; g6 X6 T% m# c has an optional argument. As we are in quoted mode,
/ Y2 ^) b: H6 s- O8 {- T+ I, R0 t5 q) o/ e5 i7 q
# an empty parameter will be generated if its optional! U- `$ _$ i1 A8 }) q" x
: ?/ U7 w2 _8 R1 f* c9 W* ]
# argument is not found.
5 m9 U3 i- L- n( M7 Q
' q0 H/ x7 P* J4 y* W& Tcase "$2" in$ t& O8 D5 B; }4 D
) X& ]2 W8 G+ O$ w$ u
"") echo "Option c, no argument"; shift 2 ;;1 S* t) r* h! V& Q! i
2 N$ u9 t/ ^( p, z, v; F*) echo "Option c, argument \`$2'" ; shift 2 ;;# w, X9 @' N P X+ I! V
2 }+ a a; r5 t3 K
esac ;;1 ~& ~$ ~/ a! I) ^+ A
. I( \2 ~' X. i3 U3 [
--) shift ; break ;;
@; X+ e* K+ Z6 G
2 \& J7 h# M6 U0 Y+ M*) echo "Internal error!" ; exit 1 ;;
: f. W* Q; }! ~# I1 H3 b( X
& o/ s& ~/ ~6 O8 h# x" Z' Q9 [; vesac
- t2 R H; Q* A k/ X% Z: Q, H1 C! l2 v6 C
done" w7 F- ]( f& z- b7 B m
* k2 y& x9 X' _% W% e! v- [' H+ Gecho "Remaining arguments:"/ C: F7 S, A( G1 `4 a8 _( j9 \
* \# Z$ L }3 f
for arg do
' C. d" l, M* z1 x) Z8 R+ b: b; N4 |2 V; G9 Y: O" z& T o, G* g% f% P
echo '--> '"\`$arg'" ;/ l# _2 M) J# z" P
' N- g! p' C) }* k: z
done$ _3 f$ [$ m" Y. o# K
8 k3 H$ `. n4 A' X
9 f/ r% N1 q6 F/ A8 x: K. @- X
比如我们使用2 z3 j8 \! }! U% i
1 z0 p# r4 c" B9 K0 Q7 C- B
./test -a -b arg arg1 -c
+ A2 g. j( b1 X5 O! h! t1 |" m+ Z" Z1 M
你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
( c& H. j; R' `9 a9 V9 x* h4 _. h7 ?& M4 k
-a -b arg -c -- arg1' x! o" e: @) y
1 V# |+ ^, z2 Q6 ~# ^1 C W% y& L$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。
$ g0 L- K7 m( F
, n" S* T- Z! Q: I' f0 d# p- g3. 总结$ W1 f4 [$ F6 {, x
0 w, l1 ]- S# I+ B. V豹尾。. Y/ z8 L& t& x F1 D: ?
7 A3 K8 D& Z' y" E' v4 U
本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统
- ]/ L* I1 g+ e+ s' ^: A
# ?" E: u( \) g, q+ n |
|