版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
' D J$ S. x9 y
getopt/getopts:Bash中命令行选项/参数处理4 W3 j* r# a0 C/ G5 I5 ~5 T/ G# Z
0 C. I* Z9 a7 k8 V4 |5 @0.引言
5 [7 G4 |: E# I! |, s4 q- [& Y% {, A. z1 [% F$ ?
写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
$ L; j! ~6 R) T
( v0 U, R, O" N" a }选项与参数:
/ d. O9 F- h" a' m6 [- t9 Q( i$ O
1 e6 z3 l6 M8 q! e9 Z+ [如下一个命令行:
' w4 u" b! I. f- \! X2 I' v$ L5 @6 d5 m; Q Z8 c% E5 u. g6 I
[vb]
+ Y1 r; V( [1 k+ h./test.sh -f config.conf -v --prefix=/home
% r5 c: \# _- b$ w$ B* n- W' M: h( u# p, g
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。
- Z# K: c! B! C& x8 ]8 A- T' g$ a- p0 S R* j2 `" t7 n$ q
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。. P: W ~, Q( V
: H% i, L$ t& ~# u7 V4 ^( k9 E! l
在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。
2 j1 w, e* M2 n
3 o3 g O4 N- ]手工处理方式
6 Y) P7 e Y# g9 U
; _; E. x* q! F* `0 ggetopts
t6 y, U/ R4 ]; k6 k4 O; O
" r9 Q6 Q/ j' `1 g$ Ugetopt n+ ]( f, ?4 r; q. h* C% x
) c) N% [ ^1 I8 z+ S% ~
下面我们依次讨论这三种处理方式。
0 O. F- u1 D6 j6 q( U9 A/ _- y, v+ t: O5 S. k9 y5 G
1. 手工处理方式
/ M9 T7 y D3 R+ S6 F) c1 E4 C$ ^! `' B9 g$ O( {
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:3 q/ c8 B3 K T
; a9 l# s" a3 P! T; ~4 y$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]. a1 v. t& H: j8 d
$1 : -f,第一个参数.
& B: w0 }% Z& l3 H7 i: ~; F3 m( Y/ ] i2 P3 a4 a' Q0 _$ `" ]
$2 : config.conf
9 ?0 u3 d. }% F$ Q$ d, M. K* R$ b" K# ~
$3, $4 ... :类推。
' v- W8 P- w) Q! p+ P3 b# F" i5 k4 Y0 N( ] }* }
$# 参数的个数,不包括命令本身,上例中$#为4.# W3 K" j+ O& i4 k8 {% d: H/ R+ f
& x" R5 ?& z* r2 M& x7 m$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home
" m( d$ k7 d8 n) x( |$ R( p
7 |; y. g' Z L. T e$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:! h5 E q4 J) w9 A- M! b
$ z9 Z% a9 B; \, B% v5 Q8 f& S+ }
[vb]8 @6 l2 t7 ?: p z6 V; I1 E. F
#!/bin/bash
$ x/ l f) i: H- B. d, X [9 Q+ z" Z6 e/ R3 ~" u6 L2 k, a
for arg in "$*"
6 x: E' P* g( }9 c" e! u7 O# `& G$ P9 B0 Z0 R
do
6 O/ l2 K p2 c1 V8 H; s( ]2 t5 L" d7 n. H( \2 a
echo $arg h8 y8 x! g, f
. u' \, Z& F& E8 o0 {5 ^% Odone
a8 g% u% ~* t- g% c0 G* D2 Y- N/ Z M
for arg in "$@"4 K* d; y3 B# Z
2 ^$ D3 S0 B) p% o! o4 \' P. u- k4 L
do5 l L! e; J* s! I) S9 w% Z( L% k
# v- o! n( H" P2 U. r: W! h) Lecho $arg8 G% _7 Z! u0 c9 {
" J' i D2 ~3 ?6 w. @2 D% Z e
done* Y, N; N* g2 `% T0 s+ C
' J3 A3 [6 B. {. l
执行./test.sh -f config.conf -n 10 会打印:
2 K% H) _7 X. j- S
3 d9 X7 B/ \3 j: a-f config.conf -n 10 #这是"$*"的输出- X* j: X% c. Y4 C
8 v! Y% V8 s3 e% t#以下为$@的输出" a# p: n. F0 R& i( r( l8 {
: h, B) `; @: ^6 x
-f! Q4 q- @6 [! z, l
/ l$ p4 U2 Y$ Hconfig.conf
@5 f& u- R6 b7 `+ O
8 {% x9 ^$ G# O C! a-n
2 ^+ T: G) f. P6 E& R# r/ S# T# A; \) ~2 C8 \+ I& z
104 f" }: a& @, \/ v7 X: ^2 _
" B7 ]9 g0 M2 e3 [- I所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
B) K; y3 ~2 H2 q5 {% @" _$ N2 S
. f" k. \# j0 S% ]“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:
$ k" R3 \/ o, y' W. v9 V' C+ R& M% ?) _4 q0 u
[vb]
7 \9 ~& C9 ^+ |+ a8 t# W#!/bin/bash$ N% \! G4 G3 x/ d9 Q
' _6 R: \8 U2 }' R/ c" M! K
#if [ -n "$1" ],参数不为空 - ]3 j9 _* \- r1 v
if [ "$1" != "" ] 0 v5 ]( k# Y' n% x% Z! \& [! Q
then7 X4 O- J" J! P% o0 U5 N$ y2 ^- m
, i7 l7 q: w H+ O5 c' p6 s
#...有参数: P# H4 Q3 G2 W; `4 `
1 d' l0 M2 {; O' X* U' O2 n
else
; x- i( g5 g4 k8 b0 k1 s) f
. V" n; t, p' D# X7 S1 hthen! [8 l# j ]! P7 _. b( X
3 ^# u2 [9 I. j% F2 e) d
#...没有参数
% Y; d& [* \6 \8 i' ^% p' |* ]( p- ?( }7 c3 J3 g s
fi
) Z4 d2 O; Q4 }" K0 o1 {5 n4 G' Q2 m: @. t! O% s
手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。
% T# C9 M2 w# u& b7 ]3 w
4 b* ?) W1 J; g7 X8 h1 j2. getopts/getopt
2 `3 x1 C8 D' C# O4 |! X4 C! `: W2 H o' I8 ?
处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:
- e. U6 m$ \8 N# \& f/ L, A
, c# U8 ~5 j# x. `$ L./test.sh -a -b -c : 短选项,各选项不需参数; q2 x" g, V" e: ]+ T W
/ Z4 W1 W! ^8 J+ b d./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
$ ]) M+ Y* L& y5 b: | U
+ A% v* T& W! i./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。
. a) `' D. k" L0 B: K! X$ w% z
, C( ^, L. S0 w7 M' w./test.sh --a-long=args --b-long :长选项
1 h1 t( x! E+ \( g0 I: Q* h- B* G
; B3 Q. N9 F$ d& L1 _我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh
. N+ F" U( d5 f1 F
4 U: A* h$ G8 z% P( e+ r9 E[vb]
. d2 ~0 U. F) e, I#!/bin/bash
# N! u- J) \' X0 `# D+ F* ^2 r6 W9 b8 ]" e8 a& Z5 _& A+ f
#选项后面的冒号表示该选项需要参数
8 \9 y. H, E% {8 A
4 f% j3 Y1 x- x; zwhile getopts "a:bc" arg2 l( |- q/ d" g2 C0 v
$ a6 O I' }* z% @% @' }
do3 W) y- T+ ~ E: M$ J& {- \; z
- c* C. f! s, ~& X, P
case $arg in5 o/ y. G; S& W2 W
* ^( b9 N* P; w8 M( ra)
" c+ l, Z6 H6 j) ~% G! Q2 J4 s7 M) g% e& c
#参数存在$OPTARG中
W- e! Z' J" p0 R X
& O& A* }! w! u* g$ v0 M8 yecho "a's arg:$OPTARG" ;;
0 ?9 R: O! J1 m; H# O
3 H# K, t' ^+ X( T5 ~b)
$ F7 R% e) d9 u& @& y4 X+ F! o4 \% k4 ]' P- X5 `
echo "b" ;;& w3 C0 p# N1 _1 ~. G
, v' R; `: E0 x) O& E7 ^c)* Q) c# t2 S8 }. E _) H6 k
& V' x8 g! P* t' m9 k0 Mecho "c" ;;
" h7 y8 K: ]; e* `3 y. R3 l2 d# K+ k ^! K
?)0 m v1 Z% u% R- o3 h7 x
, h/ W1 d+ O0 x# I# w7 W6 S. D#当有不认识的选项的时候arg为?' E4 q! S2 z8 C/ a$ [
5 a9 `. y2 o0 C. |8 S6 f; _0 `$ P0 M/ M
echo "unkonw argument" exit 1 ;;
5 F6 q& l' L6 q \! @$ y7 @3 U& I" I; I* r, F! Y
esac
: y0 O; e9 n+ m2 z
( ]% B8 G8 V9 `* S5 X: e8 sdone
# o4 I( q+ y0 F0 i$ e7 K! y) L8 Q. `# R- b
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
" Z2 C7 |5 T2 N+ u
0 W% X! V+ M) G' c来加载了。
) G. Q% M$ O( x) V+ i# I6 V2 m+ b: `2 t! D
应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:# \! E# I0 x# R' _% y7 ?% @3 I! T
- x( t7 f& ~- A[vb]
8 Z5 q8 ^( a6 H+ v) K#!/bin/bash% L7 {. q/ W# M
: Z2 x, c0 R9 z/ T$ P1 f: a! `# A small example program for using the new getopt(1) program.
, H4 M5 D0 ~' M$ [6 a" k. d8 e- C6 p z8 M$ }2 h1 Q( D% ^4 X
# This program will only work with bash(1)
% {1 `- F4 |8 r/ R6 |$ _
8 u& e0 n x5 U! ]- b# An similar program using the tcsh(1) script language can be found
4 {+ b2 O8 O* }7 x5 K2 Y9 j- ^0 [. w, _7 Y# d6 v
# as parse.tcsh8 y' F3 x" T1 W& J+ b
9 f, h* r) L4 Z3 e1 Q. z: o# Example input and output (from the bash prompt): `8 m% R, `# {8 e+ t2 ~5 E% n) Z
& ~3 v: a0 i3 I4 z2 h
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "+ J- {6 G# g( Z8 H* `+ t
: u9 c% T9 l) u3 a$ t
# Option a6 k8 x) Y0 B H. d3 g$ H
( h9 e$ R' e" N
# Option c, no argument; G# M/ o% p, t& \8 V; W3 ]
+ V4 D3 H8 r; J; j% R) ~5 q
# Option c, argument `more'1 [: O, x* r& K
& U1 \2 Y% y2 X* a! l2 G4 D
# Option b, argument ` very long '& o% W7 U9 ]1 D* K" J' C0 C
1 g6 C+ U6 i) z5 P
# Remaining arguments:
; |5 A+ u5 y3 @5 W
, p3 _/ \& R5 a/ i3 G7 g# --> `par1'
+ s0 f0 @8 ~+ W' ~9 P+ y) ~2 Q, n1 ?' N' y
# --> `another arg'
" U+ o: I3 |! g! x9 |, x) r8 l: s6 h9 P2 z) H0 h3 V6 P
# --> `wow!*\?'
. ~8 s$ }. r& U$ |
6 y: X0 J/ O% i4 y6 j2 T# Note that we use `"$@"' to let each command-line parameter expand to a( G! r; p9 i7 x
# ^" V4 z4 {0 a4 |6 w
# separate word. The quotes around `$@' are essential!1 n" f% b0 ?$ S7 x, @/ b6 J7 c
/ }4 H6 h0 m; I% _6 ?$ ?% v
# We need TEMP as the `eval set --' would nuke the return value of getopt.# U1 x9 |- Q9 T. q& m; p: M
) c1 e; B5 R I$ G
#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
5 y6 H0 }. L M9 y, ~+ A% L: H/ O* r3 n, K& Q
#如-carg 而不能是-c arg" i) B# M$ s G# z4 j9 L
; R1 P. I% a5 r5 T1 ^( ^
#--long表示长选项; f ?5 f/ l) ?6 ]. {
4 Y% r! Q3 A( m' [3 ?
#"$@"在上面解释过$ T5 o' f0 j! h6 s% F) C, D6 r
" A- a5 E' l+ y) P- H; r# -n:出错时的信息6 j- l' u. f0 i# C' @
9 F- \# d3 o$ G; d/ E9 t$ m; i. p# -- :举一个例子比较好理解:
2 {# V. a6 X- G7 ], v/ \4 R. r% j0 |! [/ Z; |
#我们要创建一个名字为 "-f"的目录你会怎么办?' a) Z' j- E4 T0 }7 B- Y2 N8 [
1 @2 @0 q! \9 \* n& n! y) L# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用7 ]2 G5 @; Y4 ^& t, [; O
. O$ M0 }* c+ p. y# C6 a" Q# mkdir -- -f 这样-f就不会被作为选项。
+ F: @& C5 o& D& |+ A: ~$ q; ?6 D4 H( q& X1 s6 @) c4 A$ \) }8 T
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \7 z6 O7 G3 V/ z+ f- X( }6 ^/ @+ T& g
, U: P9 x5 y! s, `1 L/ x% F-n 'example.bash' -- "$@"`
2 R# t' g- j- q. @' E7 U, M) g J+ B6 y* E
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi : \' {+ a8 `& J0 l" J7 b
# Note the quotes around `$TEMP': they are essential!
" s/ L2 i& l5 O) z
2 O2 I6 t6 S5 d) W; D#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了
5 g( }5 W# i, l% o( z: k4 x4 @2 j
+ H! B g- Q; u9 d. P( U3 m* `' `eval set -- "$TEMP". p" B0 Y5 ?9 B
3 a/ B# Q; f! S$ H#经过getopt的处理,下面处理具体选项。2 k6 _# d0 Z/ n- W) |7 k D
" ?3 s' W* f) awhile true ; do
0 }8 f$ g. L" M8 E9 y6 `+ e2 u1 }9 \$ e: Y1 b( R
case "$1" in
4 X6 Q- S' R1 R4 [
2 q q7 l5 T9 y3 I( w6 @. a-a|--a-long) echo "Option a" ; shift ;;& _" @9 x, y5 _9 g
) {" E3 b. X1 o/ p( H-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
* Y( b$ B8 b: L0 `/ I7 l! F# p$ ^& @/ ~4 ]6 u! |: q4 a: _
-c|--c-long)
5 b5 K/ D H/ x8 b. g( h
H* g: ?; f0 I4 Z1 l. Y# c has an optional argument. As we are in quoted mode,/ E* O" K5 ]3 f) o( f
7 A# f E' V* ?5 Y) y$ B3 E9 A
# an empty parameter will be generated if its optional- \& X c a" N. b4 ^
/ a! R5 I3 @. B1 R- F* F# |
# argument is not found.) j( l( C* j0 n* h2 g
& z* z6 K% ~% J# l! x' B9 d7 Y2 _, K* I
case "$2" in% Q3 [ ?# B+ y7 e# w
. z5 i R0 [1 e"") echo "Option c, no argument"; shift 2 ;;
5 H. O- `9 M5 K, u0 Y. Q
9 d/ |# \9 }& _6 a" {*) echo "Option c, argument \`$2'" ; shift 2 ;;
8 V5 A; } f6 _4 K' }# U
7 q# c: L9 w# [8 W* _esac ;;
- v3 a6 Q. r! w. p1 k" I- @4 O" a* k% i
--) shift ; break ;;
% g7 C# d) C4 \5 E/ l0 g& ^$ A9 R$ F! o0 f
*) echo "Internal error!" ; exit 1 ;;% h: }9 L. m% m7 H6 ]
$ z9 O, ^! L+ ~6 z/ E
esac+ P8 d! v; v; p$ X, ?# y+ _$ z
) P {# y1 x. I) s3 H: ydone0 _( ? y6 O( W" K4 y
5 [4 F Z/ k2 J. b
echo "Remaining arguments:"& T# J8 X: B* n, p" f
; B$ a) c) Q! L1 q( x
for arg do
/ B" o+ z6 t+ h% t* v7 h3 X: X2 T8 ]$ Z+ p% W# X( `# Q( O, l
echo '--> '"\`$arg'" ;
8 m) Y K: i& D7 b- f# W3 p, Y, Y1 k
done$ S: `# T1 \/ M2 j$ j( e/ ]
- \; H9 B2 \. x. I: }$ r
6 w2 |) {: K1 r6 j比如我们使用* y Y2 q, ^" [
( ~& j- _" Z, ?./test -a -b arg arg1 -c+ S+ l( P1 B( _: H% r: D) q# W$ t3 b
; U0 r2 k) f7 T你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
" L# d" w! e6 U+ ]8 A7 {0 d, _1 s% `( m) C
-a -b arg -c -- arg1 a% L; I6 {5 Q8 l- j1 H/ c# T- ?
0 j# {7 R; p& P3 d$ M" k! c$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。- o0 h8 S8 e0 C7 W
* R0 d% }; V8 D2 P7 n# a+ l$ f3. 总结9 e! Q- p4 v$ J
, Z7 B" F$ f% c/ }. K' Y豹尾。8 i7 h% P+ t. k2 ~) y
$ f( P2 y9 _# |8 j) n本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统
+ e" |4 i! J" w, o, R0 b* I/ d# F, V9 |) }' F: R
|
|