打印

[转帖] 30分钟搞定BASH脚本编程

本主题由 mwpq 于 2007-11-7 08:57 关闭

30分钟搞定BASH脚本编程

一个最简单的脚本
3 u% c$ C! }. ?这篇指南将通过一些例子来帮助大家学习功能强大的shell脚本编程.
7 o) M8 ^6 s: g5 P1 E, ]在这一节中我将学习一些简单的脚本,它将有利于你深入学习一些知识。
* H, m3 |; F- C- I$ V一个最经典的Hello World程序 & | B) H4 m) i& Q0 t9 F
#!/bin/bash ( o+ o3 X% P% j, h# x, c, y
echo Hello World
( c$ G5 z, \+ f2 B* a( I
1 Q5 G1 W+ Z5 X8 o+ H这个脚本只有二行,它的第一行这个脚本程序由那个程序来解释.第二行只有一个简单的动作,它的功能是在终端上打印出一行”Hello World” 。如果你测试着执行这个命令时(./hello.sh),你将会得到一个command not found的信息,这种情况大部分可能是你的第一行#!/bin/bash这个程序有问题,你一定要确保bash可以找到并且可用。(一般情况下你还要对这个脚本附一个可执行权限)
8 \0 ?' [4 V, m; y0 A5 x& H#chmod u+x hello.sh + X- V1 k+ {2 {/ h
一个非常简单的备份脚本
s/ Q2 `. S8 r" x#!/bin/bash , _: x( a: J+ E
tar -cZf /var/my-backup.tgz /home/me/
* `6 _0 ]7 n x% N. w' G/ F( Q
' F6 [: `( j+ O/ T% y6 E在这个脚本中,它替换了上一个例子的将一行信息打印在终端上的功能,取尔代之的是一个备份/home目录的tar备份命令。 ! f2 W$ x3 O' T# t2 U/ s6 F
关于重定向的一些信息
4 \ |5 O( b; v5 b最基本的理论基础 , }9 J( l a9 J" O+ j, j# |, n
这里有对于三个文件的重定向定义: stdin(标准输入), stdout(标准输出) and stderr(标准错误输出) (std=standard). & v5 e8 ]; v1 z( V1 R4 \- @& z
基本上你可以 2 D" \+ a+ Z* L# O, Y
1. 重定向 stdout 到一个文件 1 ? p* F) C8 j8 j' E5 ^
2. 重定向 stderr 到一个文件 : x9 P% H: B+ e' ^' `
3. 重定向 stdout 到stderr 5 e* F, x/ ~# Y6 [( Y
4. 重定向 stderr 到 stdout
1 ?$ N' r- ?9 \; M5. 重定向 stderr 到 stdout 中并且成为一个文件
9 x& ]. `+ H. q, W: C" V6. 重定向 stderr and stdout to stdout
/ M7 U/ r( U9 ~" e7. 重定向 stderr and stdout to stderr
; x- P, T1 q( N( vLinux中 1代表 '标准输出', 2代表’标准错误’
4 q( Q( Y! D& ~+ N* ~5 ]标准输出
7 l0 t) M8 A* d* g, m9 k' z这个例子将会使ls的显示结果重定向到一个文件中.
1 V% l% F" P! `5 p) _0 Z! X7 B! rls -l > ls-l.txt
( r& L- q8 T; h$ O. ]! d" A$ B2 ?. e8 v
标准错误 & ~. l3 a: d) J' C
这个例子将会使grep命令在运行过程中出现的错误输出到一个文件中
& d1 O" G: f6 E# ^. ]7 k7 r8 sgrep da * 2> grep-errors.txt
; A7 y/ l7 {9 B$ F0 f; p/ G" l: X- F1 k! o- c' k0 Y! F! i- n
管道 9 D c8 Q% @( n# J! H( P
这一节我们将解释一个非常简单的并且你以后一定会用到的功能,它就是管道。 - J2 D' g$ x1 t) S; t
为什么大家都会用到管道? $ K" V1 v2 W5 W b( o
管道可以使你非常方便的将一个程序的结果转向到另外一个程序中。 . q! |7 x7 ^# p. j
一个sed的例子 : T( y8 p6 g+ J, O
这个例子使用了非常简单的管道功能: ( @7 c! q l* b6 O. }/ h
ls -l | sed -e "s/[aeio]/u/g"
5 _' _3 @& N: ?$ T% b" s* q" W& d) U4 f- }% a4 G1 ~( ~
当我们执行以下命令后:首先ls –l会先执行并且它会输出结果信息但是如果它的后面跟是一个管道符的话,那么它就会将结果重新定向到sed这个程序中,sed使用了替换功能,所以这个例子执行完会,会将ls –l结果中所有含有aeio的英文单词替换成单词u. 7 V3 ^- l* K) ?9 f$ Q% S
通过另外的方法实现ls –l *.txt " o% n( A+ y' @5 L" H; u+ h
也许这种方法不同于ls –l *.txt,但是它避免了出现一条 No Such file Or Directory这种信息。 ! K. V5 H( D1 [# O
ls -l | grep ".txt"
4 E8 }! o/ v" E6 U$ ^
: j" t Q! e& q$ ?, c8 p5 r" r当ls –l 执行后,它会将程序结果输出到grep这个程序中,并且去匹配.txt这条信息。
3 G7 P" o" t0 x2 e' e变量
) S9 h& G2 W ?& [; _你可在任何编程语言中使用变量,但是在脚本编程中它们是没有类型的,简称弱类型编程语言,在这个变量中可以保含一个数字,一个字符串,一个单词等。你并不需要声明这个变量,它会在引用这个变量时创建它。
% @, V, P* N# @1 x: X' G- `) A使用变量来实现一个简单的Hello World
/ D' _0 D0 }$ [# r: y#!/bin/bash
- h; q" N0 E: Y" JSTR="Hello World!" 3 U, U% @, H. M( L
echo $STR 9 y5 g! F1 r/ b/ p/ c: l" o8 v# l
# X- H" H: }: V( `
第二行建立一个STR的变量并且为其附了一个“Hello World”的字符串变量值。当在这个变量前面加上一个$符号时,我们就将会得到这个变量的值。 5 u: L K# p5 J1 \1 L# X4 m% ]/ [
例子(一个使用变量的备份脚本) + | z2 [, d. D; m9 [+ C0 V
#!/bin/bash
( v0 w9 b9 y1 H3 @: T/ tOF=/var/my-backup-$(date +%Y%m%d).tgz - ]5 E: D9 r% }6 a/ f
tar -cZf $OF /home/me/ 3 q' F3 `& q* _7 E, V
/ E4 y5 C" |# a6 b" L
局部变量
( Q8 ^7 V* p' ~/ a6 D- l局部变量的生命是有限的,请看下面的例子 & n& ^) C! G: v7 J% g; r3 m
#!/bin/bash
5 H& I+ R2 z9 Z# Y9 aHELLO=Hello
( B" m# ^0 \& w8 r, G8 k* bfunction hello { 1 `8 [3 Y( W$ ]& z; S
local HELLO=World 8 t8 ?0 `2 P: c r5 f8 d
echo $HELLO
; a, Y# x( B. `3 C} 5 c) M& Z3 F7 Z
echo $HELLO 9 C- r t+ |, X$ E8 N
hello + f) g; _- @# S+ g! w+ X% M) `
echo $HELLO . C+ C* l9 P( M7 o! G0 @. O. n

% Z- N8 z' j' [; E- p- f条件语句 6 e x3 u6 ^( E7 ?( ^
条件语句可以使你决定是完成一个运作或是不去完成一个动作时,通过一些判断来实现,一般情况都是通过一些操作符来实现判断。
( C; R1 m! K/ e3 E条件语句可以有许多实现方法,最多的情况是基于if expression then 这种情况,也就是大家常常说的if判断语句。它的语法基本结构如下:
4 ^- H: n( I# @& R! E8 p. _+ Zif [expression];
2 n, P" a/ i1 z9 Othen ' h) L+ b' `6 [6 g$ \! Q; b
code if 'expression' is true.
9 X h$ ^( f* G( x7 T* Hfi * f7 t0 W, n& ~" a5 ]4 @
一个最基本的使用if ...then的例子
2 J, B( L9 p3 _2 \2 v' x#!/bin/bash ' N" ]2 F( Y- w0 u3 t/ Q' ~
if [ "foo" = "foo" ]; then
7 Y9 |" M8 ?" s8 g4 Gecho expression evaluated as true
5 b9 [9 r- O, h+ B& L/ M, ~fi 2 N0 S4 y; y) C0 K
: ]( Q* N. Q% D8 k
这段代码执行if判断,如果foo等于foo的话,那么你就会执行then语句下的输出语句,最后通过fi来结束,这里我要强调一点,就是很多新手在输入源码时特别是在if 语句后面没有空格,在大括号后面没有空格,在等号两边没有空格,这些都会制造出一些麻烦的。 # Y0 O/ ]! I( j; N( [) A3 d1 Q7 `
一个简单的控制if .. then ...else
; n" Y% H6 C: U* T# q, X- J#!/bin/bash
1 s% K M0 u" R: x3 z: eif [ "foo" = "foo" ]; then 8 P% f& j! @1 V' O, L) c' i
echo expression evaluated as true ' i# q; n& N* z: X7 Y: ]
else
3 ]" }& ^! ~: s; X6 p- Decho expression evaluated as false
" M: b# b( r( M2 n2 `% gfi 7 I+ i8 y3 a/ S# P

: U, D/ a% r" O. i8 h6.4 通过变量进行判断控制
0 N( g4 T7 Q2 y. X6 d0 L1 ]#!/bin/bash
6 O9 E9 `0 ~' l$ a" \9 J9 NT1="foo"
+ W/ E+ W j- D5 |T2="bar"
8 g/ o; F8 j1 R+ ~& t! E9 Zif [ "$T1" = "$T2" ]; then
( }: E, _- j& o" d. B# |echo expression evaluated as true 4 K+ n/ |* S2 p+ [9 g7 c: K1 P
else
4 P4 `* w7 n* x" recho expression evaluated as false ) O% B- L/ j0 C3 B
fi 4 o) ]% p- m& N( S" B$ R k3 W

+ a) v( L" ^! i$ B8 H" Y$ x' t) j7. 循环结构
+ [! M6 z# | Z在这一节中,你将会学习for、while、until loops循环 6 Z @: p0 f7 x ^ f
对于for 循环它有一点不同于其他的编程语言,它有一点像VB中的for each语句,就是所谓的递归的循环。
& Y9 ?3 ^- ~4 W3 }对于while循环,它其实是一个加强的if语句,如果它的条件为真,那么它就一直执行while循环中的语句中的内容。
4 r: z4 k9 B) }7.1 For sample $ v f8 @( C1 ?, t/ n" c& T
#!/bin/bash $ }/ ?) b5 y7 W
for i in $( ls ); do # P9 h2 i1 p9 S( r9 I- y
echo item: $i
( c# z. F/ l1 I$ n# R" N+ G) Vdone
+ E: q& z5 P* ?! e
7 H, g7 J2 v# j: @+ k在第二行上,我们声明了一个i的变量,它将一直将ls命令的结果全部循环出来。其中do 和done之间是所要执行的代码。 ( a! x& `1 Z7 D( s
While循环例子
* h2 W | F* Q1 t( k7 ]#!/bin/bash - X% n8 f( V. X( Q9 ]- t
COUNTER=0
* Y# g6 `5 l- mwhile [ $COUNTER -lt 10 ]; do ( H t; k. s6 ^: {
echo The counter is $COUNTER
, o, [8 K$ I% Vlet COUNTER=COUNTER+1 0 ~' R- q9 [: w1 A0 i" ~
done
, h9 ]4 `1 l3 U5 w这里面大家可能看到一个-lt的命令选项,这里它代表小于等于 0 w) o8 O) D: r& R! |5 N L
-eq 等于
' M* n4 Z8 x, e3 h4 [-ne 不等于
; f3 I! r& r! F" P8 E-gt 大于
) X# a k+ p' T6 ]6 M5 n-ge 大于等于 $ C5 h" N) w* \$ b/ b. H M
-lt 小于
5 L: x% ] d& X- U* ^/ ]0 O-le 小于等于
" u. ]2 H2 v* T' z: dUntil的例子 * P+ ~) h$ x o; Z6 g" ]9 t* _
#!/bin/bash - K9 U* B7 R, H6 R6 F0 ~5 m3 i- m
COUNTER=20 ' a/ J: i! N5 i5 G0 {4 k4 _
until [ $COUNTER -lt 10 ]; do
Q/ Q+ r* z) @echo COUNTER $COUNTER v! x; G% r- v& F& Z
let COUNTER-=1 7 Z, o; O5 i L8 v
done
2 }* p, c# M8 m9 f$ r8 D# m/ H4 W; \5 M, ]( }
函数 0 C$ I" D/ G% H" r. m
对于大多数编程语言,你都可以调用函数来执行一段代码,这样可以使用减少重复的书写代码。
5 C. c: {8 c5 O) n" Q! }7 o函数的例子
; {! I8 ?" `, ^. g4 H4 }#!/bin/bash
8 h# y: p0 K; O9 X8 F6 b6 _function quit {
# Y0 s5 Y8 g% u- n2 U' ?exit 1 z* Q. u' b& v3 r9 W% v+ K+ i; |
} & B0 V! ~" D9 b$ I. [0 U5 ~
function hello { $ l! }3 M) i: S- {
echo Hello! & |9 C6 K3 p; O. A! u3 ^+ d- K
}
v7 v5 \; k h7 s) r0 x6 j% Qhello 4 I: J8 ]7 T3 ]
quit
! h$ i9 I( E- r, }# X5 t7 H9 pecho foo
2 k7 z) U; r& ?* F6 O9 ^$ ~7 ?/ k* O9 s3 j
在2-4行包含了一个quit函数,在5-7行包含了 hello函数,如果你不是很确定这个脚本是是什么意思,那么我建议试试它。Notice that a functions don't need to be declared in any specific order. 6 N5 s' J* t1 T7 X# o7 M
当你运行这个脚本时你的程序将会最先调用hello函数,以后再调用quit函数最后才执行echo 输出命令。 ) ]: F! `6 b/ \8 V4 p$ V- f
在函数中使用参数
- A4 y w" d+ M$ A' W2 R( d#!/bin/bash
# f/ Z" @/ d+ v0 u9 `9 qfunction quit {
4 U7 }* H+ r# o: vexit
9 e9 K5 t* S7 y; O8 A- V}
" c; z8 s3 ?0 ]! z4 ^& efunction e {
, s8 @6 ] w9 ?& J- u! qecho $1 j3 k' X! q" T; d
}
+ m) ?9 ]3 Z7 f# [' ze Hello
2 k u9 p, N: {( R5 S2 N$ [6 W0 ie World ! X- E7 a5 w6 m- i6 w' u+ F
quit
% _) m R B. becho foo
) [( I y) d$ w' H
; d1 s+ ^2 p, b6 i; `6 ]
; h0 q( h6 k; ]- f. h2 J: P6 c这个脚本几乎和前一个脚本没有什么不能,但最主要的不同是e这个函数可以使用参数了。
" ^) J1 P7 L9 v( S$ k1 V用户接口
3 N5 m9 e/ @6 W* N4 Z1 r/ a# F- {使用select命令制作简单的菜单 # r+ E% @* P) S' V* d/ R3 q
#!/bin/bash # Q: k ^! W O$ w% \" f' j
OPTIONS="Hello Quit" - |6 I7 Y! ]- T2 ^" y; G- W
select opt in $OPTIONS; do 1 O, {3 t5 c2 e6 U- x; A
if [ "$opt" = "Quit" ]; then ) d0 i9 I& d8 C* F" C1 S
echo done 6 N* w% K8 [- Q2 W. _
exit 0 u# V) X+ g# M" T
elif [ "$opt" = "Hello" ]; then
" n! \9 P* _2 o# x- I- Y) }: qecho Hello World
- A/ d! p& g3 _ O7 W! ^ Belse
2 j/ n7 ^. w7 i( j" ]1 oclear
# s. ?6 u8 }, C% E, A Xecho bad option
0 x7 e% e* x9 s1 l, z3 m' Ofi
1 }% b! R1 V+ H; H& k# kdone ( x' u G/ E: q1 [

7 ?2 E/ X$ X7 s6 B: @如果你运行这个脚本你将会看到一个非常简单的菜单
7 f/ l& G) e- [9 J' j使用read读入用户输入 2 D5 s+ R& k$ t# O/ s$ N; U
在多数情况下你可能会用到提示请进行输入这种状态并且有几种方法可以用。这是其中的一种方法: 9 U: w& I* ?/ V* `- J
#!/bin/bash & B o/ u7 t' X
echo Please, enter your name
+ u5 A5 [% d3 Gread NAME 3 e8 v! j, K" Q
echo "Hi $NAME!" . c% F n5 Q# E8 X% Q
" y% V0 V3 ^. B1 u
对于变量来说,你可以通过多个值来读入。
* u0 j; c, N, J#!/bin/bash
. H4 v' r- Z+ S/ Z0 O0 F7 X: Secho Please, enter your firstname and lastname
1 N7 ^3 c7 r3 i* \read FN LN
! q1 P) v. }% i; {echo "Hi! $LN, $FN !"
! J1 w4 q3 j% P4 W0 D+ \, Q
2 w% W6 i& ~" q$ O% ^ {算术操作
# z) f& }6 q: W6 H! s: s1 D使用以下的命令:
3 T7 H6 @, C' s% ?% Q8 b: hecho 1 + 1 & E5 Y/ p+ }5 _4 l1 Y6 f" E% p& @
如果你希望看到2,那么你会很失望,那么我们应该如何进行操作哪? echo $((1+1)) , G9 m' [1 j5 @! `5 h7 ?, ^% D
我们可以使用以下方法: & R( p* G. m7 e8 O
echo $[1+1] 3 |4 J3 n* V5 I, J1 m- k P. ~
6 A0 ?7 z9 m. e6 Y# _" a" m! W
算术操作
: T% i5 _. ^ |! Q/ D+
7 }# v+ [& @& F" [1 [- 1 `0 ^% s d5 K: j' {% v; G
*
/ T" T5 [2 z# [1 s$ F/ , b* c$ Y8 J- L2 H9 a
% (remainder)
+ t! Q: ~$ i% g3 s" `+ q关系操作 2 U2 |% f0 ?* i( t
-lt (<) 0 Q! T, G7 z4 k
-gt (>) 2 L9 ~. e$ I* _( r' B/ B+ d7 s
-le (<=) - R4 I1 [0 u0 I% t
-ge (>=)
O o$ I- Y4 ^- m5 G7 \-eq (==)
1 v+ Q- ~# `0 a2 U' I4 s0 X) J% S$ l-ne (!=)
欢迎大家到认证区参与考试题目知识探讨,提问有奖励,解答更有奖励。
电脑培训教师交流群:40351571

TOP