版主
主题
帖子
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
, l- ^) F1 L( H2 \# c' egetopt/getopts:Bash中命令行选项/参数处理
. [5 E3 |9 w" l. d
) A- [, W }) z5 A$ y0.引言
T* G- Q7 Y, B9 B0 d h) u
% Z7 f/ ]: `* c写程序的时候经常要处理命令行参数,本文描述在Bash下的命令行处理方式。
- G! ~0 o: C' d: t
% _# B1 N' w' ?: {+ z" w o6 z# E7 @选项与参数:
' b N, i5 t7 y" w" V7 |! {( r6 t% k8 h2 R- X& J! v& c( f
如下一个命令行:
0 @. T5 ~* n0 q" w- y% d# V U, W* r4 ~
[vb]. ^8 Y) I+ i. ^ s3 n
./test.sh -f config.conf -v --prefix=/home( G) G l2 ?9 a" K
% F) s; z$ \" M4 z' q1 V
我们称-f为选项,它需要一个参数,即config.conf, -v 也是一个选项,但它不需要参数。
% t/ @0 [" Q, T, {# A( K' e+ k& |# d, Y
--prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,/home可以直接写在--prefix后面,即--prefix/home,更多的限制后面具体会讲到。* c9 D/ r% ^' k% Y' M- ^/ |
/ P9 e& o1 U z! F在bash中,可以用以下三种方式来处理命令行参数,每种方式都有自己的应用场景。! L: c U- L0 ^3 w, d( B6 F" N
- b8 v1 |- _5 D! }# }
手工处理方式2 @$ x+ ]' e1 M
5 h5 L, e! ^& {0 Q3 N* M! B6 Z
getopts4 k" N; e" N& i2 ~+ i; T: \! F" E
3 Q( g. l B; q/ z8 t
getopt& K% h& R: y" e7 J( M( a
& r$ ?7 H& H( b下面我们依次讨论这三种处理方式。( M$ p0 x- E( e) M/ f! H
! x+ q) J2 w" j/ B0 \+ e5 j1. 手工处理方式
; P' V$ c: @% m$ g8 ^9 b$ g9 t9 s- ]1 T0 t5 s$ ?6 w' ? [
在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:
& a6 ^* B8 I, G' Y( Z) `) L( v
; t5 j2 f9 _8 `0 [3 c$ c& B/ X4 o$0 : ./test.sh,即命令本身,相当于C/C++中的argv[0]
% O/ E/ _, d5 P$1 : -f,第一个参数.
0 E9 i/ N1 U" Y: x' K9 v
: [4 A4 A( |+ f% n* _$2 : config.conf
7 w& v5 q& s0 [
2 y! p6 _- A. h' m$3, $4 ... :类推。 J; v/ n* K' q
, n/ k3 q; b, B$# 参数的个数,不包括命令本身,上例中$#为4.
T6 L7 i4 o2 H1 a# m5 b. x8 s% l+ I& W0 |2 B% o0 a
$@ :参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home
9 {+ U8 h. P2 e! x0 f5 `8 {1 O; @4 k2 ~+ H/ R
$* :和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。如下例所示:: \$ f; u5 b! @7 k. \
. {$ ?$ ]+ ?' z! n[vb]
$ [8 Y3 f/ _6 |/ W#!/bin/bash
. q# }' m4 F+ Q" [3 o* F& T: C. k ]) ?9 ?# X, q' P
for arg in "$*"7 t4 H6 L6 ~* E4 Y: c/ `, C/ L" h
' q4 O3 H0 j, l8 h$ q
do
& W- Q4 g5 q2 n& j8 S# r; m' F) h! B+ R& ?9 ^! A( e7 |, |
echo $arg
6 l6 G+ f5 u! j6 l) b
' e% o `/ V( M2 r6 X, ddone0 M8 H* o) u# ~( G% n
' D4 k. o8 h, h- V% ?/ K$ k" ]( }: L8 g
for arg in "$@"
# ~# |2 C& J' R$ r: H
( w0 m5 m) r* i) Pdo, p4 `$ v& v9 p. Y. F
6 y' l8 M+ b5 w4 K8 Oecho $arg) g& z7 M/ P4 ^* R; p+ l
: G" F7 X o; ?5 A {! x
done8 j3 N5 Q2 l$ v6 y8 [$ x! A* j
0 t8 H3 m! g' ?+ ]
执行./test.sh -f config.conf -n 10 会打印:
, u7 X/ a/ r+ `& r; Q' g7 L( V, Q E$ L( \3 b0 z9 I3 e/ ?. y" _
-f config.conf -n 10 #这是"$*"的输出2 I+ w2 m. A+ x5 B) Q5 V
|6 F2 u' d- b$ e. o) ~#以下为$@的输出9 ^: [8 Z6 h9 W. R9 N* D S Y6 m
: m! r9 D/ F" D: o
-f0 L) a. s& X! M" H
" u1 N1 l6 c" p5 J4 R( |7 Bconfig.conf
: O" }, s% x* T1 @
. \, L- [" v8 ~-n; N- }5 G& I- K
" |4 i4 @& P/ V# o
10; [; n1 i5 j N* \3 F3 U. \
+ A x* X- {9 { m7 m' k8 a' |所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。如
5 V! t. b" s* G# G+ _
: e7 C0 [2 ~3 ]" t& X! G“./test.sh 10”,而很少使用“./test -n 10”这种带选项的方式。 典型用法为:# i$ z4 i6 }8 t% `
' v* X/ I* I N3 o- n
[vb]/ O$ j0 e* k$ o8 ] i$ L. z
#!/bin/bash
1 N. E8 N" ^3 {" s U5 R# D" F! w/ ~
#if [ -n "$1" ],参数不为空 . C7 W0 Q9 P& H. T. t6 l/ u; P
if [ "$1" != "" ] 5 `' B4 z5 _4 ?1 O0 n& w4 y
then
0 G: _$ R9 K, o+ C* M# {
6 n p- _# y/ a/ j8 x# [#...有参数
4 Y% M" W1 K- Z/ a, M, ~' _3 U U
else
, a% { E( y3 q% w
, p! m/ O) W" W- i" H# Qthen" K C( m Z1 b5 c' Q! x" }3 s5 _
0 i! x* h; Z' J" G: q3 ]/ u#...没有参数
, e: f Y1 k+ m
, a& N ~- I7 ]" D s5 nfi
9 ~2 \! G! C' V; D2 L. s7 X; r3 N$ E8 M9 G( l3 G' L/ s8 B
手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。1 |6 d1 H; w% ~ ~, ^' K. I
, }7 i! m1 x8 ]( @8 B2. getopts/getopt3 V1 T* k; H: `$ J6 o+ v
- F! r) G* o* x2 o# V
处理命令行参数是一个相似而又复杂的事情,为此,C提供了getopt/getopt_long等函数,C++的boost提供了Options库,在shell中,处理此事的是getopts和getopt.getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的。先来看看参数传递的典型用法:
; m7 ]6 F+ u4 M- t5 r5 `. g2 M: U( q. e! @8 l
./test.sh -a -b -c : 短选项,各选项不需参数* M+ C+ P% Y4 d! n# u$ x) V
) d4 J ~8 Y" E3 ?7 ?0 \# L: t- ~: [) S
./test.sh -abc : 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
6 n7 ~" k' l0 I: d% N, z/ _1 v% b" Z' Y1 S% n5 l5 G: y8 f0 }5 M
./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。! F% m2 ]! ~, M+ x& g2 f2 ?
1 { B }% W6 U4 `; k
./test.sh --a-long=args --b-long :长选项
: }. K; ]# v2 a! [* h# P
) M: i8 M7 M( I! L我们先来看getopts,它不支持长选项。使用getopts非常简单:--test.sh# m* ?$ H M/ c" y2 ^, e
# d+ f5 [) r" E) K[vb]4 s) u" t4 u2 E) H7 L
#!/bin/bash
; u+ N, E5 I) K( |2 n5 U7 S8 h
' B0 O7 c J& t#选项后面的冒号表示该选项需要参数
; h0 Y. m* K) j& j! ]% i' s9 T1 G0 [. e$ n" q) i5 n3 ]
while getopts "a:bc" arg
. _2 C9 @" ]% B g c, y5 @' o2 L8 {6 ]4 v. ?1 a) Q
do2 r6 f0 w; S! y
4 E3 ]* m: ^, {9 w6 K, dcase $arg in
1 W8 D' N2 R& C6 |0 U* j: W; w
% _, f2 U& Z: T! {( }a)
- ]0 d% n S t, S! s4 q% S- v& w$ C, }* @/ n
#参数存在$OPTARG中) O( }" c4 B3 _8 h. \! X O% ^3 ?
& P: Q) F/ K- E, M. E
echo "a's arg:$OPTARG" ;;# c2 Y0 W, y6 |; Z) ~5 j$ Q `$ I4 o
d7 k" \' x2 d- \; [8 Ob)
0 ` g6 g; Z6 R& j$ x
: q, {! |4 D6 Q# ^& }echo "b" ;;2 G& {0 J% G" O8 \4 a1 ?
6 O% D9 T' m+ |" i3 o6 T6 O" Kc)
9 t- ^' U% }/ {9 R) }4 O: N) n" @# ?$ |' g, k% A
echo "c" ;;
; O9 q0 M* ]$ o" `& g6 h/ K9 J _5 A* ~7 }! L
?)
/ D" d. H5 c" a V7 W$ F" y7 w9 n5 ?
#当有不认识的选项的时候arg为?( b, \7 n4 B6 O2 g
- F' ]( d. s- c" k0 n6 a. q
echo "unkonw argument" exit 1 ;;
3 U( |3 r7 j, V- L
1 C9 E" i& l6 U3 v6 y' F5 Wesac
( @( I0 ]/ q2 M# T
( t6 D: H, b1 x4 ^' Odone
2 Q* z# p4 x) s* N' H9 K6 y( B/ D$ I* R' R" r
现在就可以使用:./test.sh -a arg -b -c 或./test.sh -a arg -bc
9 g, Z M3 X1 j/ z4 I. X0 }/ [7 ]7 ?$ f
来加载了。* y( `8 V X( y+ b8 b
. | f! `1 }; c# P# B {2 {. b
应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.下面是getopt自带的一个例子:
) K3 n' g. J! l, V7 x* b V
% H/ {# r7 D5 i+ I[vb]
' Q- F; p# c: u& I1 G( k7 Q#!/bin/bash
& H* C' d* K6 B) S) e) e ~ ]# S3 v& r( a
# A small example program for using the new getopt(1) program.' d- @" O/ S, q7 A
1 T7 ?* x+ h" w* D# This program will only work with bash(1). T7 R( S+ |: G( d* E; y" |5 X
- @5 U8 P& p& }% Q. F
# An similar program using the tcsh(1) script language can be found
% ]; g. p# \+ @" `! b$ F4 h
/ E, d$ A% Z$ O: m* L. s# as parse.tcsh* {0 ^3 |" ]7 q# n5 r
' ]1 U' D! q/ W2 Q
# Example input and output (from the bash prompt):0 k, @) I3 |7 c9 G' C0 v$ @
: ?+ a8 m+ i J+ i$ ~5 l: _
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "% h/ b7 n1 O9 X/ w+ L
% c+ H; L: k$ D0 k; D& i4 Y7 O6 e
# Option a: u( e, P1 B t. }. c3 |, [
# U. B8 T. p/ f6 d3 a6 o3 p
# Option c, no argument
( K9 W! B, N$ g$ a% h6 g. z% ]% l
/ Q$ `) U* @4 j) m5 ^# p$ V# Option c, argument `more'( d1 z" f( A7 V# J5 S
, i) c) _$ f! p. C+ X# Option b, argument ` very long '
0 ]' n o) a; R P5 }! f6 e; Z* h& ^: v- B/ P+ V
# Remaining arguments:: n7 E' J( A- c J3 Y
$ m+ k! w( Z' ^" x* e# --> `par1'/ o7 n; ~. k0 v4 s! n* b4 V
/ `! @- g3 m t5 _2 S
# --> `another arg'9 [) z6 W8 X; N8 N8 a8 b$ {
% q1 F( _/ w. D d$ [
# --> `wow!*\?'
4 O2 z+ d- f8 B( y% s; {5 h1 r# A8 v# t/ `
# Note that we use `"$@"' to let each command-line parameter expand to a- h( Z# o& j/ N5 ~( U* G% @# v
4 `6 L. ^* g4 |, h( h5 O. T8 P
# separate word. The quotes around `$@' are essential!9 ^# g2 y# p( Z; z
( n" E6 K. w9 ^# We need TEMP as the `eval set --' would nuke the return value of getopt.
, g3 I# f, D2 p
! C7 K; Z! E: X% H, D0 z/ l" j; t#-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
; L+ e! [" l3 H
' d7 R! k8 R k! ?3 {0 T7 y) s/ B#如-carg 而不能是-c arg
' I5 W6 e/ {' ]4 q& }& l: T
/ g3 u3 q) Q5 H5 a, E#--long表示长选项4 ?7 O' z5 X9 L- M+ e+ ^: |/ F
q' M) g) x3 B+ O) J#"$@"在上面解释过
' F0 y2 D* c2 ]- Q0 C2 O
" C# o& W& B* }' M/ i# -n:出错时的信息( H9 q/ \9 k5 W' Z& K1 N' H2 l
" J: f, j$ r; g0 d* j5 w( @# -- :举一个例子比较好理解:
9 p' M& _7 [. F* j$ U. V. _
( |: w# T6 r5 b. f& Y$ B6 E#我们要创建一个名字为 "-f"的目录你会怎么办?
; l. X' {* n) W0 Q; X& t, f
( Y$ f3 R D) O. q/ Q. s) D# mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用
3 b9 P3 `/ K0 T3 a4 B( [1 c
: f, {7 I) ~4 T# mkdir -- -f 这样-f就不会被作为选项。
! i1 s- m& Y: k: v: Y3 f+ G1 J
) S* L8 x) m5 T$ P$ l7 w {$ TTEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
! D$ q- M: t2 L" o5 O2 x! ^1 ]& R0 c- _6 C$ |4 z% q
-n 'example.bash' -- "$@"`
$ ^* c! ?7 k* s. u
( o/ ?# i- T0 O" t2 Y1 ?' Kif [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
6 C0 r% C! x$ Y- i# @# Note the quotes around `$TEMP': they are essential!
7 _3 x9 a D |( l1 q- ]2 k6 T3 j
#set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了$ C8 ~% z0 W* q! G' ?
; j8 i- B- H$ O: S* geval set -- "$TEMP"
/ r' B8 p. M- f! B+ u7 L
# t. m2 o4 i3 q: s _$ l#经过getopt的处理,下面处理具体选项。+ F+ E% x, D% f0 V& s
$ b% S7 e! Z5 a* |# j: u
while true ; do$ n [8 j4 |; a9 ~9 R1 s
: X, C; Y1 ^ ocase "$1" in
1 T. r4 B. X1 D9 N, G+ ]& {5 P8 V. M' m: E1 E, R$ `; d" T
-a|--a-long) echo "Option a" ; shift ;;
3 g. f3 n, U4 R% v! z
% B! O5 b7 n. c- \-b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
8 e7 a( S9 v! J0 m; F) r2 a3 B J& Q K2 N% G
-c|--c-long)( w/ {( d6 I& b; U8 v& Y
% P, y( ~$ h8 `( @, I. d" {
# c has an optional argument. As we are in quoted mode,+ C) `- m- G) P3 ]) d& u4 P& V/ h7 c2 k
P$ Y; e+ l t% c; m) \' E( P, E# an empty parameter will be generated if its optional2 Q9 B# n m; u* F% X/ ~" w; Y, v
- |1 X3 k0 x8 V% c
# argument is not found.
) m+ W2 J; e$ Q: H- n0 z
. U" C, S) ^+ r' acase "$2" in# l, t0 s# V! z, ^; E2 J- s
& N! ^ L4 n. L7 N% A"") echo "Option c, no argument"; shift 2 ;;5 {! K5 j! v( M+ J/ `) c
5 t, m X! q; l
*) echo "Option c, argument \`$2'" ; shift 2 ;;8 ?0 B1 ?- \3 E9 [$ z- j4 d
+ H$ r0 M1 ~$ T8 g8 P7 Zesac ;;; H+ ]9 ?* Q$ s8 l2 r: U
' |6 s& u5 x/ _/ ]& u4 T6 l
--) shift ; break ;;2 g/ O. q& H# j' M
+ Q+ l" M( t# a( Y*) echo "Internal error!" ; exit 1 ;;: r* t% {) J {% J0 n6 b' H) Y
" j4 H8 w/ p9 f4 s6 V7 |9 Resac
; {8 J% [7 W5 s. I2 `4 e5 N' F- f/ Q8 l$ W8 @& X1 l
done
8 P5 L, e. N; y. K1 h7 S* l% m+ [4 N" I2 N' H
echo "Remaining arguments:"* S K9 ^ _/ a6 j" u
) }! `, N' T9 H
for arg do
2 y+ l$ p5 d$ h+ N2 ]' E. S; O! J6 b7 S8 e" s8 E' b
echo '--> '"\`$arg'" ;4 }& W3 x* M. x9 r% M
* V/ w" w2 {7 Z( [' }/ adone
) x! D, M9 N& ~ z( E& V# e
% g* O& W9 L" _! \
" I0 ]0 O! Q3 I, D2 m* |比如我们使用
0 d& D9 v/ Y* s0 g0 M
$ c7 A8 V& l! J3 ]& z! I+ X./test -a -b arg arg1 -c( z6 \$ Q: d; Q& d) @2 O) A6 F
9 \; Y. o- H! n' j. A* r3 ^你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:
0 `1 C3 V. O! h* a
7 H/ t2 g6 m Z' b2 s-a -b arg -c -- arg1- I; u" B2 T$ Q8 M I
3 L. S) y; @) X( j4 E$1指向-a,$2指向-b,$3指向arg,$4指向-c,$5指向--,而多出的arg1则被放到了最后。
: w; f6 X4 v2 A0 M
/ ~/ ]. l, s: r4 i% N% h K Y3. 总结: k6 |/ P' k1 E7 k( a- K& P$ s! b* r
6 `% g' m" M* X4 D豹尾。
8 ~! i) X' | \" i1 ^8 U7 M! ^1 f9 N5 P4 f# \5 T- O
本文系统(linux)相关术语:linux系统 鸟哥的linux私房菜 linux命令大全 linux操作系统
% t$ t; h, Z$ ?
+ G' w# ^ A: H6 [- B- {2 O |
|