版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
- V! @! t" i2 c/ v- N( K Lgetopt/getopts:Bash中命令行选项/参数处理: l/ l- n( n+ X& y$ t1 l
2 A, @/ I; ?! D3 U) I0 }
0.引言
6 e# ~ Z! a1 {, H0 p" n! p L& K& g c
写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
! z# u! @& e* k- \! e, ~% [) t/ B$ T% H" L* Q4 X; {
选项与参数:
8 d$ D- t. o$ {: ?# w0 r9 `/ V+ i( P/ d/ |
如下一个命令行:
& @& u/ e4 l. L. u6 M- M+ ]; H8 }( y
[vb]
5 d$ @- N% o$ Q: `. v# J0 M./test.sh -f config.conf -v --prefix=/home: ~" T8 L o5 q* H# p/ {
! u9 [ }7 k+ {; o1 I
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。3 a+ p+ ^5 @7 Y4 T9 f3 C/ t- k
2 G* j! @7 }1 a- F" e2 b) M! X--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。
* N6 V- e5 r4 p6 {- i& k. g' g
+ m8 P, X; }, `4 w. l在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。% J$ @" t, r6 B
3 A/ H: `' i) R% u" W1 T
手工处理方式! R1 ~5 m- B- v1 G5 l1 A
+ y3 y/ n3 M& `7 ]5 i7 Pgetopts* V9 _+ e6 D% K* L- {! Q
% w) w4 q8 J i1 ~7 ~getopt
- M+ z* F! c" v8 S+ M- a! l9 \ X* M5 W! [8 M1 m6 |( H
下面我们依次讨论这三种处理方式。: O: x Y; }( m3 U) R: i# `
* m% U# `' h9 r( x1. 手工处理方式 M( M; ~% k' ]' j$ A1 x1 d W+ _
, r7 [) t; I5 P5 n" V: a7 R3 {
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:
2 f/ n* M- y; Q2 U: X, `
9 u5 E) ~+ ~( a. I+ u% N0 Z$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]
1 I1 g7 O3 Q. G$1 : -f,第一个参数.
2 u0 O+ \3 u* h3 S$ T# V: d' o, m' v) e: v" X# k% @ [' g
$2 : config.conf9 W' W$ V6 D' P0 ^ P; y5 ?0 L" O
: \$ k* x Q, O$ s0 K
$3, $4 ... :类推。0 n4 E$ d" ?) W/ t: J8 L
+ l; l' n. G2 M% G$# 参数的个数,不包括命令本身,上例中$#为4.
5 `) H) {( Q- O4 B6 I- V% \ I9 [# N( N8 {: o; }
$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home
0 o+ k8 b& T% Z$ M6 @* s q9 q& m r4 F- Y' l, a
$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:& v" B j' O9 A: b9 J
3 u9 x6 a) g z) B/ |0 n[vb]
2 I" c" y6 ^! b. S/ ?#!/bin/bash
6 V& B' A4 D' v+ J8 _4 F
5 X9 h9 t- l- C% lfor arg in "$*"
$ ?, H& @/ Q. y! b, ~4 @: [! D1 T. t% y
do
# y8 U9 g2 W5 C3 x- y |( X. }+ Q& @; t7 R
echo $arg
) r8 w: I o% n6 c7 L5 \+ Q- H. P& A3 u0 O
done
3 r; V- R' D& v5 t" v) ]
$ U7 j9 R3 t$ j( [0 S$ b7 dfor arg in "$@"
" c" s1 j5 V* l) w. G% s$ q; T- O _+ ^5 a' p. b4 p3 O
do
6 x+ k" _) p7 F0 r$ P) N3 n+ O: Y3 E
& P* H; O! [2 decho $arg
" f# Z% A v/ o* s5 F. `; k6 m' @9 Q( k& h. d# ]% w/ Q
done% f- b0 M* l/ B! c5 x0 _$ _
! G; k c7 F \7 \; ]执行./test.sh -f config.conf -n 10 会打印:+ D+ x. M' F8 `' ^ N
" m# V, }% ~3 ? b4 W
-f config.conf -n 10 #这是"$*"的输出
# N6 R! j2 n* @3 m
" `( m% y2 q2 f, x9 b* S#以下为$@的输出
2 n4 H! D" u! G7 t$ b
' L/ e- ?) N9 p8 u3 {) y' t* Q: u1 F-f
: D) r6 f2 M3 k. E
( K z2 F* ~4 _/ b0 @config.conf
, l( W r& _$ z$ U2 E
% @3 ~& [) F( L9 o0 o7 j-n
+ W9 A2 ]5 {/ H( D: N
5 h1 q2 U W- n, ?* B' b5 C10) g3 B( p4 c( X$ T! P6 E: C
$ m$ ?( Z( J1 S6 R所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如6 k4 `$ o& k% u% M! E# |6 ^
0 O& H1 q) I( N6 O, A4 c% f
“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:
2 y$ `+ B9 H3 ^ R2 B" m9 ~& o! T% s5 R
[vb]
" M5 Z5 y4 u# n2 h* W+ J1 O, R#!/bin/bash
" F# N' Y. e" u# A( ]( b [. Q
6 D+ R% i& `+ u#if [ -n "$1" ],参数不为空 4 f- P, t3 Z( [/ _3 a$ e' c
if [ "$1" != "" ] % H8 O( M8 V' T& k( d, o
then; Q6 g) ?6 F: Z6 z* i
& d; \( Q/ W2 g( x5 n4 L#...有参数; u0 ^' L% l x
+ e2 ~& A. O1 E [1 ^
else
% Z2 U- c4 q# u4 s ?0 V
. w8 U" z# m5 b) K/ ^then
) U: V9 `: Y2 t2 S1 Z3 f) I9 Q' }( l; [/ \7 D; W: w, w. q7 R
#...没有参数 F `, X/ T7 w
, \, T9 l- f% V0 T: ifi: ~( P/ M/ y+ ?4 `. Q* s
2 o, Z& W, P7 e2 k: b1 ?手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。
- H, C/ w" l) F- O# b
8 \( A) ?: |' }* _, n8 @; J2. getopts/getopt& Q7 u. y3 m8 j8 {
7 O2 W c$ s; g: v- n) O8 C: Y/ g处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:
@4 ^1 G# N, x% t) u9 G. S4 T& u3 l1 y) ^5 V' E
./test.sh -a -b -c : 短选项,各选项不需参数! J8 _% J% [$ |, q7 r& o& a" T
2 S1 P$ ], o6 ?- k5 B./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
- B! A8 Y4 N- p- S: [- q7 Q; p- I5 I. p9 J5 I3 ]3 |5 q
./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。
1 n/ U8 q3 d, t1 m" O* r8 ]* ?5 q4 f6 t
./test.sh --a-long=args --b-long :长选项# D7 N3 }6 S2 R. V3 O4 l0 `0 Q
5 k) Q t" I% m6 C0 D( o. Y$ U: X* J
我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh
: Y: M S: j% C
; M9 P* r" K) x: E/ Z7 q) _, [[vb]. V' F+ \9 a2 X4 P* u& X
#!/bin/bash0 u* I- S6 W$ m% l4 `
+ V! i0 l4 I! O3 R7 z, X5 U#选项后面的冒号表示该选项需要参数
! K( m( q4 r& J
1 Y( ]( C) R7 Y1 y2 A/ }- Ywhile getopts "a:bc" arg" M; }3 M5 Y* L' C' d
q O9 X7 h3 K" z3 E* xdo
% ~/ R) L& C! W! L! P3 \ x5 K/ e! y
case $arg in% J0 P, r& [3 g
/ p7 n$ F8 I6 n! A& y! k8 Ja)7 R% }" Y7 i6 D: ?
- F$ R& a3 X8 J( B#参数存在$OPTARG中( `8 u2 B! d, b5 w
7 ^9 ^2 b3 A/ b# W4 zecho "a's arg:$OPTARG" ;;7 f' f+ v3 B, u, n5 _' {
+ _' k0 Z- |: E/ o( \+ c/ z% d9 g9 @: mb)
, F) N! M* R/ T3 ]( o
5 o" }% ~# m7 _7 Y4 a# secho "b" ;;
5 v. n& R6 E! w! l* z; u5 P2 `+ d% z
c)
- {* x) v/ F! D
( p, x, A! L7 d3 a) q% V2 j: E x recho "c" ;;
. |+ A7 o& v) e
* } J' W) ]- a3 k) R$ M?)
3 v- W! j/ [9 S. w+ s/ ^" C$ U3 w0 u% a3 {6 y1 s
#当有不认识的选项的时候arg为?: P, v; |4 G& |) s+ {2 t% v, ]
* \2 x- V" U* j5 Q' Fecho "unkonw argument" exit 1 ;;
( [% C0 b: w, w; E' T6 W6 t3 H
* J: }% \+ h! |% T2 R" {7 t) Iesac
7 o! o- x$ A9 e& [8 a0 y
( i4 L7 ]- y E1 wdone
0 `( i! x/ ]8 c0 A6 D- j4 g- Y2 k- u/ N, g3 F
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
; J7 B* i) H$ m8 ?. n
3 f$ R+ L, U2 z4 y) h来加载了。
% s. ~/ e' g& d% [: h1 H" K: Q+ b6 p% D7 a' W1 ?2 e& Q
应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
8 e* j6 Z* x0 B+ ^3 Y g; `5 L. ~8 g
[vb]
4 y8 E4 v4 V7 D* v8 e' _3 F+ n#!/bin/bash
' h- T3 @4 E. ]! t
( e! f! g: \! Y4 }# A small example program for using the new getopt(1) program.
8 M# O( O4 {. F: p5 G1 L* x& j {4 u: d
# This program will only work with bash(1)
. f7 b' a* {. [- a5 G" d; N7 Z" t- R
# An similar program using the tcsh(1) script language can be found
4 }! Z* F8 s; e( }1 s
" [; a6 R' w* J4 Y7 a# as parse.tcsh0 v+ i0 Q! t% a+ }, Y K
1 r$ C$ x: k2 \0 k% O, Z# Example input and output (from the bash prompt):
1 c4 P/ G9 l9 \# I r
5 \7 ?4 \, N/ P0 E) p0 N# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
; v8 h; r* E1 \3 M% f
9 q+ I+ j8 P$ R' I# Option a- Y; d7 |5 l$ w
+ b$ s- i! j" u( o0 I2 d, I' R# Option c, no argument& W6 A% u+ v+ i g% R
, D' c: h, V" y. |, Z# Option c, argument `more'
6 }- h3 L6 I4 i' E3 G9 ^5 [: y: o9 i5 o- F- b! J
# Option b, argument ` very long '0 ]) D+ w/ U" U" T8 L
& w, O4 R% m3 {
# Remaining arguments:0 L: w0 c8 j% t$ V; k
! _0 A- c8 t9 z! [! v! s" \
# --> `par1'5 X8 n7 `7 N5 o9 E; L
" o, ]8 r- p. W+ A# --> `another arg'7 @- j; ^1 D- _3 e0 X1 F D. I& I
* j. i% u% j0 f. B l$ ]# --> `wow!*\?'4 r1 S7 a1 ~6 A" w! J- Q/ a2 h2 _
9 f* V5 P' N3 E" `+ w# I; d
# Note that we use `"$@"' to let each command-line parameter expand to a
4 h+ n Z, t v; u
5 r) }9 z! f1 Z* {# separate word. The quotes around `$@' are essential!# i# X0 _; U0 v+ |; K
# x( q6 v! A( [/ p/ X' w8 p. b; X# We need TEMP as the `eval set --' would nuke the return value of getopt.
X- ~6 d4 d& v d+ B; y9 q9 o* ^
- Z1 v8 s7 {0 C* m$ {; ?2 O#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
! \/ t% c$ B& }& n* `4 X/ E) O4 q$ Q3 H5 g
#如-carg 而不能是-c arg
/ A/ U V' z. t: d
: X, r- ?/ N/ A! s7 M0 R* K#--long表示长选项
0 m1 s1 H$ L) N7 ]" D$ b( z0 F$ u* _- O/ Y6 e) M' J, l8 j) J
#"$@"在上面解释过
* Z1 G" b$ o4 j% ^5 b' I( x, e# G2 O$ ^
# -n:出错时的信息
( g6 C8 {. B. n# C1 t$ X# k$ s! @. y+ W$ i7 W( h; B& l; d- i& i% k, f
# -- :举一个例子比较好理解:0 R. {+ P% W3 m5 _9 m
1 ]+ Z9 |' N( w4 b
#我们要创建一个名字为 "-f"的目录你会怎么办?1 a" t/ ?, |7 R/ B% w
4 W i2 d8 w& D
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用
( |8 y- W9 I$ S
' [( V& C( p, A/ V# mkdir -- -f 这样-f就不会被作为选项。
+ z7 I K! S/ c: N, v3 V# F; F. _
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \$ ~* {& z$ U3 \- e- K; |- e7 O- A$ b
! N$ y: V6 s5 F
-n 'example.bash' -- "$@"`1 E' y f$ @" x5 O/ t
4 Q% K) d/ {8 H2 t8 L
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
. T4 ~ u9 |/ f; Q2 l4 t6 A$ c; R/ o6 F# Note the quotes around `$TEMP': they are essential!
: r2 D( V: L! Q7 O& X6 }' H5 n$ Z1 D/ z0 v* r
#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了1 B* F6 D7 b, x
, D! f4 [; ?1 w8 f5 K5 teval set -- "$TEMP"2 J+ f5 ~# P7 I3 N0 _
0 W( K8 I0 a, p" W7 d2 h
#经过getopt的处理,下面处理具体选项。
% c1 Z* q% Z. H, b4 |
) l s0 y; r( P; m) Awhile true ; do) A y+ I( V5 t; W* e, G
" U5 Z' _7 {$ p& [/ j* {$ n9 fcase "$1" in
& k) t. Q' l3 C8 g
. e7 V4 P8 q* N5 X( k: H T-a|--a-long) echo "Option a" ; shift ;;, T2 [$ o+ z1 z# T+ a% O3 O
2 l, m( n6 L* i
-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;8 m0 i4 A1 Z) B+ L" m( C x8 I( B
* B7 ~1 X0 c" A, G0 X-c|--c-long)- l' L' y2 `* f$ t! U; o' _
Q* w8 @% @' r
# c has an optional argument. As we are in quoted mode,
$ z) R- m3 t7 U: w* e4 j
7 w( `, {- F5 n( j" ~! \+ J# an empty parameter will be generated if its optional
, ?8 j l0 S: \; k# E% x- U" x# b
# argument is not found.$ T" o8 J, p+ D J/ ^$ e
" o K/ v; d" k: N' y5 m
case "$2" in
/ E5 {: _# r+ p5 c9 } n. K1 u: L4 i( G) [9 W9 r
"") echo "Option c, no argument"; shift 2 ;;5 r- m7 O0 ~ _, L6 k* r8 F" h
# }7 Z8 t) B( E7 o v6 S0 H
*) echo "Option c, argument \`$2'" ; shift 2 ;;4 f( k8 b2 J7 z& J# a
P/ G$ ~+ v0 f& \6 [2 E: b8 B
esac ;;
* V0 L5 {) s- n% R8 |4 I/ X# H$ H) w/ P5 e8 p% C* E
--) shift ; break ;;! H6 c8 U7 H" Z3 s, h* `* Z$ } `! d4 h, H
* I: c: M- v0 k& R9 R# `) G. r*) echo "Internal error!" ; exit 1 ;;
. h: ?7 T) C) C8 [, A1 ?; B0 B, f/ J6 J" ^# ?( H' S
esac9 k" K/ Z3 n4 s! E( T8 B# h
& A! a6 v9 Q- x# c: C0 Hdone& I/ G7 U. v( r; i0 O
" s) ]! R2 t O ~6 r( o* x& ~+ O
echo "Remaining arguments:"
9 I4 l: i& A7 J/ A! g4 D/ S" ~8 K7 J
for arg do7 b5 Y" Q8 p3 v) B" d7 j
, S; }. t, W p5 e) b4 |
echo '--> '"\`$arg'" ;
5 t9 X8 V, F: D* i3 H+ C$ J0 w. M8 M; Y, ]8 o: e; i7 J
done
; ^& T5 r$ y0 w9 u3 z' O
* _. i) U3 O6 N" {
+ m2 D( T S* C) O) `4 G比如我们使用
9 d+ p6 g1 A0 I+ S% B* m) d6 Y9 l
% g% H$ q, K: M7 P' d./test -a -b arg arg1 -c
: |! X d: ?) ]
0 ]/ a' O4 R+ d; s. P你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:. X0 f6 L5 c5 }& |& O
" t0 r- ~ y7 v t0 z9 F
-a -b arg -c -- arg1
( W/ b+ A- R0 p3 Z5 F% E+ k, K: z* p- E. _" L, y. J2 C
$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。" n. L: n+ q/ ~ W
5 [7 I: q. _9 C2 j/ K
3. 总结8 N% O/ x% Y! ~' _ f" n6 z
. r- c- g6 ~+ O
豹尾。/ X% E! x7 H% B+ K8 e! k
$ W/ D7 A* K: W' ]+ h! ]7 I
本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统7 ~( d) U5 D: n* q* X1 \
/ D' y' q+ v* H. i/ v" `* K |
|