版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
3 [# H: r* f+ U. `( agetopt/getopts:Bash中命令行选项/参数处理$ U' Q5 V0 w2 n6 w6 t4 ?: ]) e
+ L8 F4 h. T2 A2 V7 x, Z0.引言+ o' H7 c% J: |2 W1 G4 X* w
" `( r' o4 e! h5 S1 k( Z, L写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
) x" c( `7 ~" `! w6 w7 M/ _
% d( Y1 a$ S5 Q% w/ V) d* u' j选项与参数:
9 m# M0 j1 a* l" I# B3 W' A" ^
$ q$ n9 c/ J* B2 y2 J如下一个命令行:2 _1 A# s+ ~8 @ U
3 _" B: s- x" P. }, y7 x5 J1 q! r& w" m
[vb]- l, ?% x# E' e$ ^9 l
./test.sh -f config.conf -v --prefix=/home$ a/ ^8 D0 u1 z) P! n
3 T9 F- o) e& C5 h0 T9 Z0 f3 j
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。
4 R( d! ? _, j" v
, w- ^6 G- b* Q--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。
, F3 Y# O, h. b' R
% s9 _0 L1 e/ `" E W' e6 F在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。
5 a2 I( H' [+ x6 ]/ h& `' u8 H# E$ D" O- H5 P: `
手工处理方式8 C6 }4 i1 x! v& S
7 O" O& b+ r5 w
getopts
+ O+ ^. p8 B: n" f/ A
4 `& ^5 Y: O8 U8 y: ?& Ogetopt) j3 o2 U3 ]- d! B# P0 g
: V" N& A- k5 o$ [, `% n
下面我们依次讨论这三种处理方式。
8 z& E$ U, b+ h; S* {4 O. [6 w( \! L ?& R2 m# B
1. 手工处理方式
% W& Y. Q! _9 ]" E! z" Z9 c" s- {; u+ D0 H5 R4 X- {
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:! f: ~& b3 C4 C6 H
! S, O5 H c: M/ v2 C; ]
$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]
; d4 ` p. [+ ^% Q- K3 X F1 Q$1 : -f,第一个参数.1 U2 `1 b4 K- N# Y I
; }& _6 _7 q4 Q9 b$2 : config.conf: W5 _3 k9 u- O" G
! @ F! t& F9 f0 ?3 Y! S# y! S& p
$3, $4 ... :类推。* Q+ h1 P! ~6 X3 F6 s
% C- p) K" u$ h# w* M
$# 参数的个数,不包括命令本身,上例中$#为4.
$ }5 {" o" T, L: v T% l8 l- @+ ]; N: Y
$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home8 _; Y1 ]* P. w, |1 a, }
4 d K: s* N8 J, v. _& U5 u4 V2 Q
$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:( A/ W4 l! W1 w) Q6 E6 M: l6 o# H
3 T0 K$ [% p+ i$ \4 ^6 G+ L- @
[vb]
( }" s# v/ J& Z4 S/ i2 R6 B u: T#!/bin/bash
i! _1 J' p! Y: f% K D/ t6 ^
; v+ h; ?6 |7 y6 Q7 zfor arg in "$*"" P" J$ J* z6 t% D# F6 Z
! T, _5 l# z- r7 R" m
do; b4 b, y$ n* H% ^( E4 b
1 [8 K1 a0 k0 i+ R% y* E
echo $arg6 \( H" f3 i- M* B8 }+ I
/ ~8 g" C, {# U3 ^
done
* E8 N2 x; w3 i1 e! U; L2 s( j' n0 r, j! W
for arg in "$@"' s$ ^& p6 R. Q; b/ r
. e1 f/ ]0 K6 U, ado
9 q8 [. G% o' x
& |- g) l6 B" ]; {. jecho $arg
$ e! C" F1 W4 O3 s7 F8 w+ X( ?. v0 m) \& S& \
done
- ?3 O3 A4 U) Y6 d- _2 g5 Y
; x3 F! [4 s$ H6 T, {% t执行./test.sh -f config.conf -n 10 会打印:% k1 m" X4 _5 I! l; ^3 P) _
q0 Z; r4 m- b4 Y6 c, P: o
-f config.conf -n 10 #这是"$*"的输出
0 u3 \3 M. M9 U
) ~4 X" v6 Q* s! Q$ [#以下为$@的输出" C* M( o+ f& [! X- Y
5 x* w3 \4 D4 {$ L6 S. W V; H2 z-f
0 Q- f# c0 A, c% L# V; [. W$ a
config.conf
: q; R1 }/ @. N; {, C$ p
$ P" o* W# d9 w" n" g-n( V$ y$ c/ X* ?
& O( u$ s0 t+ f$ R( V10
, a" b, m6 U, D- T; P# f
, |0 M1 n: v. {# w所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
* r% d) A" L: {+ }" J- `
+ H; ~. W) g" i1 K6 Q“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:
! n/ ]$ E/ X u% ?8 q8 ^$ @& a) m
* H7 j6 J9 g% L2 R5 o[vb]
3 B) p9 A! h9 i- s#!/bin/bash! c5 g7 Q* n, M) y; s
4 K& `: n" B3 O: f3 ?$ z. A9 ^#if [ -n "$1" ],参数不为空 : r$ b2 a% [' K+ {8 B: ^3 G8 G% t
if [ "$1" != "" ] , x. r# l8 w; n* l" V, V) P
then; D1 S9 z5 f1 U( ~! U; o
7 |6 T# ]- [* O! ]+ }* |! N) T$ K
#...有参数
n& X2 B" n* R6 P- }0 W' t$ o! ]/ h1 f7 y6 P
else& q5 s: v* ?3 x! g
8 T8 r1 W& O/ G }. s4 M
then7 j6 a: u' Y0 U, k* v m" z
1 e/ i4 U0 l7 L7 p1 P$ r: _
#...没有参数
U2 I3 D0 `- y+ V4 Y3 s! ~8 m* f* m8 |
fi5 V1 E- U) I; n7 h6 e
\3 |! P8 o, y1 q手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。
# i; S& \% P" D0 l2 M3 s, l6 _! T; h4 J. w8 [
2. getopts/getopt
( k7 [* { U' U+ q6 p' z" x
* u% A" i w7 O: a, F$ K2 H9 f* O处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:, s0 y/ _7 q) v- ]- Q
8 h h: k7 \) t3 `# N$ M
./test.sh -a -b -c : 短选项,各选项不需参数" F5 M( q( R: \1 g' [
/ `! l# V) I7 P* A) [9 t; d! n. e./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
) p2 C2 g8 f6 D/ P; i, Q& h
: M" [5 a" g- g, w* n; \7 ?./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。# P! x- B; o7 A* m& S
' a5 n/ d. q5 p+ f( d0 J9 b$ l./test.sh --a-long=args --b-long :长选项
1 k, G8 r$ s$ m, `- f: E6 t
1 h3 y) p1 a; \# ^我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh
- x' r+ | X4 o/ j1 `
; P2 k: y+ _) d[vb]% g& H9 Q; i M1 Z- l) @
#!/bin/bash
% Q9 S: P# b/ _. X3 S/ P
# B: k3 ~- a! m#选项后面的冒号表示该选项需要参数" u0 r% u3 J Q' P# f- M1 z8 d" @# n
q2 U" o6 ]/ U) v5 U! a2 b
while getopts "a:bc" arg* R' \7 O. B: d
! Q% o" u# \$ N- @* |
do
& |# p0 ?' A. t6 I, Y2 }& {# ^ L9 Z% t
case $arg in) t2 k3 Q2 T$ n2 n$ T3 ? J
, f; C/ t$ i. h) xa)
- u$ `0 x9 S ^$ O; E! ]# b# B7 c
#参数存在$OPTARG中
$ V# c' b, y l5 j" v# I1 L2 E! z; O
echo "a's arg:$OPTARG" ;;; E' D7 K5 w$ k, L- G5 t
* n) K3 R& c- B! J, }+ T' q4 F0 D9 lb)* e2 F k# ^* P( c4 B5 R
9 A6 B1 u3 Z$ P& u+ ~" P0 A
echo "b" ;;( J( z g' J% j( b, z T: I! k
9 j% Z4 `' h% Z6 T7 H
c)- v% T5 X# m! W D0 x. U( s' f
, e/ `: r# `+ C4 F1 O" B: @echo "c" ;;
" U' I# p {1 M9 k
! d; n/ L" L. C- u G7 o. C# g?)3 r, G! n* P6 A& X/ B- {
6 Q0 e0 i9 \8 q9 c. ~#当有不认识的选项的时候arg为?
3 k' o1 @/ {, F9 ]
+ A8 w# L# g" g2 ?* K L K% A3 techo "unkonw argument" exit 1 ;;8 p( H- c; o7 |* [$ ]; m
, F7 D2 @/ V* d9 a2 R' r6 Yesac
. p- X0 @6 I4 X% L1 o, F8 j
J/ i* B( F5 Pdone
9 q1 P/ @8 Z9 @1 i3 `. Y8 w
$ g$ I% W6 f& G5 e" g现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
. W0 v; I7 e# j1 w
1 c- c8 O# ]* `: ?# s来加载了。
! {. M1 a- ^' j; Z5 x
: q7 x: W# w: V+ _应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
* y% c9 i! g7 {" A& N. N: f1 ]% i+ D2 h: |2 G# `
[vb]) X( d3 K, z; }5 U; E7 j2 \
#!/bin/bash2 }5 r( B& D' P
# [ W1 ^+ m- c4 ?; E# A small example program for using the new getopt(1) program.
$ G0 B- c* _3 z; ^$ P! O" m/ f# @: t1 _% V
# This program will only work with bash(1)
7 b" o( U8 I! J7 V; v9 v
* S/ a# J, _) \" H$ b2 r. ?# An similar program using the tcsh(1) script language can be found
$ h0 R% b' T$ U/ k" ^" h% P! y' Y+ ]) j; I3 {7 ~. m; P
# as parse.tcsh; O+ ?# U( @3 r% [
; A) i: Y/ P+ A* T: `( \# x# Example input and output (from the bash prompt):- c/ q2 b( n, U* s
3 _7 {- M' ^( U/ \8 g6 p3 E
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long ". O* k7 T2 j, b0 `4 ?: `9 O
& d. O7 U! t3 Q8 r9 p; K
# Option a
8 U9 Y: J7 u+ K/ Z+ ^8 \3 P* z+ ?! \: Q( f Q' g2 L' G
# Option c, no argument
, ]5 k+ _! E6 k4 ]1 i
9 z+ \1 E8 T1 T* y' h# Option c, argument `more'" T7 h. L3 v, W/ }3 z
! g" S: C8 f9 V2 O: O
# Option b, argument ` very long '# _: j z3 M, e# u4 k) O
; C0 y ]9 h4 W6 {/ S) P+ P# Remaining arguments:
6 R# z# Y1 V+ ?1 f5 j- T8 r4 i# s. x# |5 |8 l, T$ s8 q
# --> `par1': s9 z6 M& q, l6 ~- j
: \6 O- O _+ @: z# i: z
# --> `another arg'- y. E& [3 E( k# g% t
: c$ D, ?' V* }+ C; U. H# --> `wow!*\?'
2 c. b# n- _; K7 L5 ^2 h
4 [# q& B3 w+ K6 O, u' k, E# Note that we use `"$@"' to let each command-line parameter expand to a$ P' N! Z4 P" N7 T1 W) R2 ?, a/ P
! e: u/ E7 k- i/ k0 h0 j4 W. w
# separate word. The quotes around `$@' are essential!
; {/ A% I& C- k _; F" A; v- c9 v- Z, I1 X
# We need TEMP as the `eval set --' would nuke the return value of getopt.% H; L3 ^ q( N, Z; d
$ `7 C% ^5 f+ J
#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项. p5 g( I% j! {! h9 Y* G8 n! W. {
# [; ^' u; v, N$ H. m' Q#如-carg 而不能是-c arg
2 k2 u! Y: X0 Q0 M X; c/ x5 w2 F z1 }) e! a
#--long表示长选项
' f" x) P/ y. v6 ]4 ?$ y
. H, p# B/ e0 ` ~! G f, ^5 N/ Q#"$@"在上面解释过( K" u9 |- D& {0 A( K5 k+ r q6 A
/ i8 U0 O, {0 p7 V; U3 [
# -n:出错时的信息
5 U+ G1 g) ]" W5 r- O4 H9 @$ C" \5 a
# -- :举一个例子比较好理解:
% J# x, ]% V7 N& p( }9 Z x- p9 {0 m: u/ C' C6 C' F
#我们要创建一个名字为 "-f"的目录你会怎么办?1 B/ v+ o5 ~5 H2 |! A+ ]2 g
4 [0 `8 N/ t7 S; v
# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用
* u' n3 P% T" F* s! t' [/ G+ m& `! m
# mkdir -- -f 这样-f就不会被作为选项。, L9 k; Q; L: ]' P7 a3 i
* f" g+ I9 Z& a7 K% t3 e2 o1 v
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
& i# A0 u9 H# o! M) l5 B( P+ h9 z1 D$ A) t* v$ O, x! R
-n 'example.bash' -- "$@"`" y) |+ r, y0 a& o) h$ [" q! ?
- ~. L) m: u/ B7 N# o- g& r# M
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi # H9 s' W8 Z: _3 E
# Note the quotes around `$TEMP': they are essential!
Q. r7 X# I8 ]4 l+ k7 u" V2 g
/ T7 a2 O$ x6 N/ ?6 Q#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了1 m: N+ Q2 |) L+ B
# k$ U" ~& t7 a3 c9 {eval set -- "$TEMP"5 M- _4 a& ~4 t! r1 Q( B
; d& L7 f' [! E" B
#经过getopt的处理,下面处理具体选项。
, Q. q5 v0 t2 i8 k j# _' z
" Z' h- ~8 k! K. ?while true ; do
K; f5 j8 P/ F: _: K# j6 m! ~
* N+ u; t. I" ~, Z& tcase "$1" in7 x" N% Y8 p" m3 I, }4 y- p/ e" b
8 P- w& z3 u$ G3 u+ K-a|--a-long) echo "Option a" ; shift ;;
$ r+ h$ T7 ?5 i, }
$ D c% b. n- P' i2 ]-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;1 Q5 s# V7 ~. F7 T" J4 y% E
5 [/ z" |: D" c' L3 ]3 G8 j
-c|--c-long)
# Q8 K. m# h5 ]( T e
9 U1 {6 O8 A! q, b1 ~! j# F# c has an optional argument. As we are in quoted mode,5 G3 T& a5 d W7 O# }: E( s
* M/ N1 q7 I+ \& V" K
# an empty parameter will be generated if its optional5 ~# p. g) E }' @. o; C3 V o" q
% N) u8 b8 G( g) O8 J0 G( p# argument is not found.
/ O% n+ @1 M- F# G5 W; B8 v$ _0 \4 N0 c) \6 G! Z. }
case "$2" in
- j! d( }0 M6 O& C- }+ u* n( [& [; g' @: c3 e$ ] s# J6 u
"") echo "Option c, no argument"; shift 2 ;;
n3 E% r( a& Y1 s# q7 u/ @- p+ b3 I( _) _& k5 H- W# [
*) echo "Option c, argument \`$2'" ; shift 2 ;;- s' z- c* E W* Q$ G+ r
8 [8 x6 E6 ~, Iesac ;;% m9 a l& u0 s4 e
' R; Z! D: b& i9 [% m9 ~% n1 r--) shift ; break ;;+ F" w0 Z7 K1 W; i
$ f6 L* H* L( X$ i8 Q+ D+ ~*) echo "Internal error!" ; exit 1 ;;3 _3 e3 k4 J" ~
8 t$ Z* V# D% x% Y# s0 N5 b
esac
$ k5 J( a3 h6 p4 ~, u2 V/ a2 s5 f3 R2 i: _: I
done
6 H" k6 e! y5 o* S! A, e% J$ X8 n, A. ]
echo "Remaining arguments:"/ C9 T! n* F6 _) r6 h. R
: _2 P- S6 E2 R; J
for arg do% h. A6 V" _) q
( T" v$ l& s3 G9 t6 ^* H6 V5 g
echo '--> '"\`$arg'" ;1 s% X& q* [( ~! t' A2 ]
% T4 B. n: \. j, A1 Xdone
; I% P2 t; D1 I: @
& U# v. S2 V. d, B
, L9 F8 d/ C4 |* P1 v- e" F$ \比如我们使用
( v( f1 M: f. u' x
]) k" {1 G2 r2 `./test -a -b arg arg1 -c
, H7 X; d9 H' \/ b6 B3 Y) ]3 T6 _7 D8 R) l" I
你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
0 M" J! l4 D, `1 G2 m5 \/ H4 C/ \- e1 R N4 z! }4 @8 d! o
-a -b arg -c -- arg14 F, E/ d. y6 U
l0 Z# N8 G7 o* q
$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。1 P6 c) b8 C" o: T" h3 z
2 b+ h1 U2 x4 o' H1 |8 e" g
3. 总结3 A9 Z t2 [4 _4 h* h( ?1 s
: T5 `# {1 D/ }5 p1 w, A3 I豹尾。
# `5 j5 M* p. f7 j& {" z
2 I6 C0 O, `( _# m$ b; b4 `本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统
$ a- F. O$ v( w
* g/ K; f: M4 U0 Y: I) h |
|