版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
! A: @! D+ d0 F+ j& G$ r1 @getopt/getopts:Bash中命令行选项/参数处理
# h% @- ]6 J, Q8 b) T {+ j
& @ D. x* a1 q/ y, a7 T0.引言) e0 u; v1 A# s( t
0 N D' W F a, H- z写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。7 V/ M3 f7 G a6 D8 ?0 T
4 c8 c, t U2 Q
选项与参数:+ T7 \# \3 o! _( g: I$ v+ a2 l
4 z! l' E+ r7 x& W
如下一个命令行:
+ E Q) \8 ~: i8 c8 |2 z: |7 O# J- R$ m
[vb]
: l4 V7 p% ^9 @: D) D9 y./test.sh -f config.conf -v --prefix=/home4 I% R8 m& d ]
' j" t7 M8 B: _! {6 _3 R3 @
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。* G+ @" ^# H6 c4 |7 P
( \+ K% U1 w7 q( W! r$ [6 I3 a
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。
8 s% k& o7 S. S0 w6 Y- Z+ R; s( ~' x* \( H: v8 q2 ~
在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。
$ q7 u% Z/ ~$ d) k: S1 `. ^- W8 c$ j; K: U% ~2 r4 o8 j
手工处理方式
! q6 ?% Z0 s# a
) ]% I: t* f( v, o" [getopts; J( u( ~ K- m) k) b
L, E8 _/ \1 R o
getopt3 L6 ]8 d* U0 {: e! Q: i) k' c+ L
- P# E3 C) T8 H9 j1 o) B4 l
下面我们依次讨论这三种处理方式。/ G0 Y- e! y* e% D& b
2 \0 L9 `2 Q4 i9 y7 O4 w' z1. 手工处理方式# X5 r+ @7 n$ \9 a
& v4 x+ J* L1 k, a* e) |; B4 ^* f在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:; o% {1 U/ o Z; a' r
/ f4 g* m3 k! X. g9 D: W+ T2 X
$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]
1 q1 |" y% n* _4 E. q- u$1 : -f,第一个参数.. x9 q. @4 T# {
- m& }6 Y' P( {5 L- r) Y! ]- w
$2 : config.conf. o! }: ~% p( p# y% E: k! u
. W$ Z. _$ T. [6 X3 i6 B7 R$3, $4 ... :类推。* X0 R2 Z" U# e4 O; {
& U8 p5 O4 `- w g% G6 U$# 参数的个数,不包括命令本身,上例中$#为4.% f! G' ]+ I9 A% q3 W$ o3 N
. a$ A# U: D& a, C
$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home
5 m R# _8 ^ I7 z; U( f4 A2 b8 {- i8 B! z* q: D* ]0 `
$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:
6 y4 w% U3 _+ m( w' h
1 W0 t4 [& M5 M, y[vb]
- B( ~& ~5 n, b1 y8 c2 G% P3 I#!/bin/bash
1 _+ \4 t0 X' h2 m( p- I6 J$ v, r @
for arg in "$*"2 Z& g% ~6 f, W& c
# W& V, _ p. A4 r/ x0 ydo! X, G: w# _* ]8 Y; C$ B# U
( {* x8 D g8 U9 Y c0 a4 l$ b4 aecho $arg
% X. i, b$ p% w. q0 a$ g
$ i8 z1 T. }& I/ J0 m! Zdone
2 l1 G9 }; H: E. z' B' J2 D6 n8 u# k- @ |4 Q) {1 N
for arg in "$@"
% b+ i. q/ i0 Q$ Z
- c( V3 Q: H3 W# |4 m7 U; Jdo9 h/ R' R: v- B! m5 o( g0 X
6 y5 l% x2 p) }% u! l* a& a
echo $arg
& e$ @5 Z# E5 T! I8 Y4 j1 u4 ^! W5 l* _# i _3 t
done4 J# P5 c# Z5 T
- ~4 j9 g2 N; p
执行./test.sh -f config.conf -n 10 会打印:
9 u( J$ x+ A8 N- A( t& H7 p! j5 o7 W( r( T, V3 l6 o, |- w
-f config.conf -n 10 #这是"$*"的输出
2 U x' t3 d* i: t8 ]( z7 a
. v# R9 T `! r! N, p( g8 T#以下为$@的输出
) ~: J @4 G- U, A0 j. E
6 s# r5 w' M" ` j1 X1 r* H-f
0 W5 n' |8 O% ?# M
/ a) Q( ~9 K Q6 |4 |% |" N! {config.conf' z* f5 @% X7 t+ Y1 D) `( B: u
' F. Q$ s; t a% w-n0 \# U7 z, ?0 ]# d! V8 L6 k7 Z
l! E/ ?0 Z- m9 O104 B7 \- I8 f$ K D+ J
4 a! X: X1 n; r8 L$ [所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
' R6 i) a8 {# q+ i
) Y( Z4 G9 p2 R A# A/ D6 b* H9 G3 K“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:# R0 Y* M( u% Q% K7 M& ?
' t# `1 Q+ }3 L* r) K3 `4 Z
[vb]
2 ?$ J' v" `6 _6 p; y" \/ [#!/bin/bash
$ E" k0 o3 U+ P! K' D. S/ k- d
7 Z5 D/ T8 v9 p, u4 C#if [ -n "$1" ],参数不为空 5 }" c- c2 X+ k/ H: C
if [ "$1" != "" ]
- r6 a4 ^: ~. v1 ?& ^/ pthen
( o) Q. a2 `# l) r& l- a$ g8 W' o+ S( v
#...有参数
7 @4 z( h' G9 v8 m1 E; d6 I: G8 P/ c5 o, h! j5 d
else$ w5 \0 @7 L- a' @% {* r. H
7 B' v r2 x# T3 ?' n3 Z9 vthen& ~7 K" T |) W9 g) s) B( D& \
' U" H7 N4 {9 ^
#...没有参数& Z6 I7 ~4 C' e
/ t, A: Z% @! G8 E. u; Q/ j
fi6 w0 T- o4 c8 I
; h! u& o9 a/ Z, C: W手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。- z( @' T) J6 n, c% S; \
' {$ ]: ?( h8 L! v2. getopts/getopt
# @! l4 w4 M. \3 N; Z2 c4 ^7 h% W4 ^1 F1 E; z) C5 L
处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:0 [ p. b9 {- ]6 e% X0 v/ {
. o. s* G8 P9 J" N$ r
./test.sh -a -b -c : 短选项,各选项不需参数
6 {( I! J( K# B1 J6 x8 H0 R' o
3 X9 T1 N, P1 s+ h3 i./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
% b3 M9 I' y$ P8 t" I: _
# ^6 E$ O' d& {* ]* e! ?) Z./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。
% J. P9 w9 W& I8 W4 `/ u9 Y. ]' ~
6 R/ p! Q; D6 {7 f6 O" I) O S6 @./test.sh --a-long=args --b-long :长选项" F/ g4 f- \% i6 C% J6 m8 f( Z! h9 K
$ E: Q! S( o3 c3 w5 G" `
我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh* H% l8 _; S( e R
* O' c3 a4 G* k. P) ~' x[vb], _, T$ C: P. J' Q7 C! X6 e
#!/bin/bash) ]4 R* j$ ]4 P$ F
9 h- }6 B5 }: g- g#选项后面的冒号表示该选项需要参数( m- A2 ?3 n- n, z1 A# \# L4 S
" Y/ }% `- \/ o7 Q- ^" R- T
while getopts "a:bc" arg9 ] @5 A. S2 I% E3 j
2 q2 M0 D1 k: v( }/ \% e4 w
do0 @/ x5 ~' a2 y( ^" X4 R
, H- b: l; s {case $arg in
' x6 P1 {3 Q U8 P# S/ A x6 b V( V f9 O$ _
a)
( \$ B: ?5 i/ j. Z
: D' V4 J% B {+ K$ f#参数存在$OPTARG中! s$ ~) p4 k$ H/ k1 e# z- Q$ T" y
0 p, D2 j. `& {/ v+ h; b) aecho "a's arg:$OPTARG" ;;
2 S- c# \' S. |- I( X2 I% W3 q3 {$ W6 E. P* C/ x
b)% q4 q8 B8 t! `) q
6 Z: B( S5 ] I; W3 F4 d, j& A; E3 u
echo "b" ;;
# _: O" q* t0 F3 w
u; ~/ H' l* M8 ^; [1 Tc)
0 K0 p- a# f+ i. s% O3 U" i
. ]- C, R3 E$ T) s x! Cecho "c" ;;
/ ^; c% U, ^. [" u/ {2 B/ `! F1 R- K) K1 g3 D; V; V; Q
?), \! N; A5 T8 X+ P" o
! a d: U% x: b7 {& O
#当有不认识的选项的时候arg为?2 _/ b0 q; s _+ E' ^1 U& u
" c4 s$ b% U8 n
echo "unkonw argument" exit 1 ;;
8 z m0 o) K' J; R* H0 [- S9 h! m( r" Y6 I; K- E' ^) s
esac; H/ _0 g, B4 P0 M7 q
* f& Q9 V& P4 k" p) kdone$ G g) \* o$ }$ i ]
* O& ^# C& p) ~1 s0 B& g x
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
: G. Z. _, w4 e$ q; V( M8 Q
) G+ T+ ^* v" u [来加载了。6 Z) L" I/ c n: c0 g% }6 ^
8 _9 L" G) p' y/ s应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
* R1 g! f- Y- K: m, l: ]# R- \5 k
[vb]1 G0 t. L$ m |5 P/ R
#!/bin/bash1 R" d+ E* l$ x
6 s: |4 r* ]# Q( j' e( B
# A small example program for using the new getopt(1) program.. F) Y" R4 m% {" E X, K
, E" ~' _9 o4 G C: T! r
# This program will only work with bash(1)0 r; ^; |* [: Z! e3 S
" C6 P# J* J \
# An similar program using the tcsh(1) script language can be found- D/ U% p B, \" ]( f3 b+ O( b
# e6 M, k) j7 H9 X# as parse.tcsh
8 J+ @ U6 r' _4 j2 t. C/ v9 \& j) D- J& g
# Example input and output (from the bash prompt):" `: v% T9 X; ]
/ K! ] k0 b! ^1 `: |, `0 T# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "; j# H3 e; G- j* b% i6 I- @
7 Y/ S* e# _1 D7 d# Option a
0 l& @. O) x* |# t, _% S* C, f) x, s9 g2 E
# Option c, no argument
2 ?) A! \2 U3 Z- V, A! l
+ ]9 l) X: @& {: s" y# Option c, argument `more'- H K: m' V, T" t+ I( m
2 T' Q: U. }+ z4 X( W& x. j
# Option b, argument ` very long '
9 b4 i- x& E( ] H+ q' p n& P' c/ o6 G- X6 F1 E' k) M1 T
# Remaining arguments:
8 X6 A$ W: K1 F. b6 w
) r2 X3 _. S4 Z! i5 u5 E* p# --> `par1'- p" w8 q3 _. O4 D/ p$ R/ t2 T
" l0 o+ w# ~% T5 O5 k l
# --> `another arg'2 c4 C" p2 v; \$ t) o( `$ s7 g
$ I2 \; E; a; m' K* l7 g# --> `wow!*\?'
/ w$ b, ~0 p! W( o% Y- b+ h: R: Y L$ j
# Note that we use `"$@"' to let each command-line parameter expand to a G% a! k6 K( V- l% G
# _0 w, K( C: h1 d, r0 {/ G$ e
# separate word. The quotes around `$@' are essential!% ^" @" R. R# q( i: {) b6 S2 ~7 R/ [* ~; n
; ?, H$ P$ [7 y- S( h) i
# We need TEMP as the `eval set --' would nuke the return value of getopt.
' T1 I( g% f: v
7 t2 K( H% Q: e9 p* N& |3 r [#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
& h* B; o) f% H: w1 M1 S# |( T% g
#如-carg 而不能是-c arg
: M$ G O& v( ~& R* @2 ]! W" P8 Y5 y8 z1 i3 Y k$ c: X" p' @5 S: M
#--long表示长选项
& _( Z# _+ ^% t6 l* A
3 _$ n4 w+ {' A5 h2 o* e#"$@"在上面解释过7 V7 ?3 R _3 }2 Y* {+ {
& ?! M5 c* e& W( e( \
# -n:出错时的信息
# p# i& a% T8 R w( X5 O' l2 ~$ N- ?, a( B
# -- :举一个例子比较好理解:- e' M3 o# s9 \ Z' R q" u- X
' N4 j2 u+ c% {# c5 O& |5 P#我们要创建一个名字为 "-f"的目录你会怎么办?
8 h3 J, k y( z' z2 V. I+ F+ ^. L' l
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用" D' g- z/ I' k4 j& L
. n }* ]5 Q7 D U h# mkdir -- -f 这样-f就不会被作为选项。
* e9 c, ~+ G7 E+ g* z9 [7 G, l+ t/ Y: U% B, L
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
: x! C+ R- C2 w5 q+ |/ s& M
2 I- G0 \/ E3 b* A& B+ @-n 'example.bash' -- "$@"`
0 \( @5 \! S! |+ V6 k# F) r3 z; K3 C
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
% u% q* |6 q; S+ y# Note the quotes around `$TEMP': they are essential!& ]( y! k# R( i% h9 r) t+ u
* j' y# g* _0 M$ n# I+ y
#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了$ |# J+ f5 J$ l# o5 S% f
9 j4 Y# Z {6 L6 o' v' W4 Q
eval set -- "$TEMP"/ e( m# u( I+ @, a( t" e* g; _
0 p; [/ q# \1 E! S& D$ a) J#经过getopt的处理,下面处理具体选项。
1 q. H0 T+ f1 y& `7 O
8 w; t/ K0 E# V8 c, }6 Owhile true ; do/ P- c7 I+ Z$ v* w' {
2 M7 v, c. g: w& j) `
case "$1" in
$ m- g, D# o8 x3 p
; |0 \$ f" ^% M* d: J A# {- L. N-a|--a-long) echo "Option a" ; shift ;;3 D4 ]9 Z) U' B1 Y5 y6 m
* \2 J3 g }4 v" p8 e8 C" L
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
I i6 t% i5 ~; v
; v. W& o6 s( {-c|--c-long)1 R4 U) r z, e9 ?4 v {# ]. I
1 p( ?1 H* S2 C1 ?! `8 L$ j
# c has an optional argument. As we are in quoted mode,* Q. e: ]) B. q. F: T0 _$ q, S9 J
/ i* _2 z" b; m$ v
# an empty parameter will be generated if its optional; B3 o1 r3 I* [/ ~1 L/ B
1 A! q* _8 `$ ~/ \5 l, _: I# argument is not found.& u j/ F% ]) a- e# x! R
- O1 f6 E4 p5 S3 c3 H+ bcase "$2" in
! u, u) J5 ^# J0 S' S+ T- x7 P, i' b( e: L
"") echo "Option c, no argument"; shift 2 ;;0 o' }% j' F6 V' `* f3 k
2 f7 c7 ?- S( S1 G$ Z*) echo "Option c, argument \`$2'" ; shift 2 ;;
" A6 a& a3 |+ s" `9 d7 e7 H
# A+ Z% _' r. e3 {esac ;;# Q9 ^; k' v: u5 v( z' T0 T
! T* m5 }. p& Q$ `& b
--) shift ; break ;;
1 s: d5 a( A# A6 ^: T8 y# g
6 A1 p' ^+ K2 |( a9 b# U*) echo "Internal error!" ; exit 1 ;;1 L8 F9 C& v& p' O0 E0 E' ?9 M" N; F
9 O+ g: h- F! @( `esac. ~5 G/ _# l* v1 w* { I5 N
$ n, F* c0 R) L- Q7 F
done7 {2 v+ Y% L+ a% r; ~
1 K3 V' \5 c* U7 c3 y
echo "Remaining arguments:"
/ V* g" ~) m6 y$ q0 y
1 I( i' ^5 w" f2 Z% C/ s, E& _for arg do
/ U, I6 ~1 u! u0 o: D1 O4 l( _
echo '--> '"\`$arg'" ;
# K# u3 x/ o# Q1 J6 A
( {5 W+ Z8 M/ J1 Edone( z# W2 Y* i$ }+ ^( l
0 s+ E8 H& W6 n. v! P8 G r9 D
+ o# ]" }7 Q5 J0 n, ]比如我们使用
4 G& V$ m( M8 I5 Q% T- m# [3 ^3 M
" _ b2 c3 V6 c./test -a -b arg arg1 -c
+ Q7 e8 V7 W& L [5 E
! h) q9 @9 n v+ H9 P你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
( G7 c5 v1 n# Q( {' U/ m! Z$ e) J- g" ^8 R0 J
-a -b arg -c -- arg1
! ~% q( {. u. q$ z# M/ H. P! z3 l% d# s/ D+ `& n- g
$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。# B. f9 e9 ^! P% F% J( T7 I, A) i
5 z6 V; g3 d% v! V# E3 q: T
3. 总结
3 J L/ _9 D# ?8 N
3 E! f9 L" F6 y; V8 B6 q7 g豹尾。
, J8 w) K3 N7 j0 G" F
) d' {0 J. `; A( i+ s& Z本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统
' ^9 Y* j6 `; t' v0 }- J( n1 t( k; S1 ^- u3 R3 m
|
|