【GNU/Linux实战手记之Emacs篇 中--Bash 编程】
作者:叶魏彬 MSN:fritz_yea@hotmail.com
Copyright(C)Free Software Library,Org(http://yea.xiloo.com)
本文依照GNU Free Document Lience发布,任何人都可以将本文原封不动的复制、转载,但请务必保留此声明。作者不对本文所导致的任何结果负责。
七、Bash语法
在看过上一次的那个简单的shell程序之后,我们来深入研究一下shell强大的程序设计能力。Shell是一种很容易学习的程序设计语言,至少因为它能够在把各个小程序组合成一个大程序之前很容易的对它们分别进行交互式的测试。利用现代Unix操作系统的shell,我们可以编写出相当庞大的结构化程序。在接下来的几小节里,我们将学习:
·变量:字符串、数字、环境和参数
·条件:shell中的布尔值
·程序控制:if、elif、for、while、until、case
·函数
·注释
为了确保Bash编程的严谨性和趣味性,我直接引用了WROX出版社的《Linux程序设计》一书第二章的部分内容和例程源代码。我想以学习的名义,我这里的引用应该不算盗版,至少它的源代码是基于GPL的。
1、变量
在shell里,使用变量之前并不需要事先对他们做出声明。我们可以在任何需要的时候直接创建并使用变量。默认情况下,变量类型为字符串,即使它所保存的是数值。Shell和其他的一些工具会自动识别“数值”型字符串,并按正确的方法对其进行操作。变量名区分大小写,这与Unix系统保持一致。因此变量var和Var以及VAR分别指不同的变量。变量命名原则与Unix文件命名原则相同,但是不允许有空格,更不能和现有系统命令重名。
在shell里,变量名前面加一个“$”字符,就可以获得它的内容。只要用到变量,我们就必须在它前面加上“$”,除非是对它进行赋值。赋值变量可以直接使用“=”,也可以使用read命令,将用户的输入赋给变量。请看下面的例子:
$string=Hello
$echo $string
Hello
$string="Guten Tag"
$echo $string
Guten Tag
$string=1+2+3
$echo $string
1+2+3
$read string
I love you /*用户输入,回车结束*/
$echo $string
I love you /*读取变量内容*/
*注意,如果字符串里包含空格,就必须用引号把它们括起来。还要注意的是等号两边不能有空格。
**我说过,数字也被保存为字符串,因此“string=1+2+3”的结果仍是“1+2+3”,而不是“6”。
***read读取用户输入的字符串,以回车结束。
1.1、引号的用法
一般情况下,参数之间是用空白字符分隔的,比如一个空格、一个制表符或一个换行符,如果想在一个一个参数里面包含一个或多个这样的空白字符,就必须给参数加上引号。
引号也有不同。如果你在双引号里包含变量,则会引起“名-值替换”,而在单引号里就不会。我们还可以在“$”号前加上“\”来取消它的特殊含义。
让我们来看看引号在变量输出中的作用:
#!/bin/sh
myvar="Hi there"
echo $myvar
echo "$myvar"
echo '$myvar'
echo \$myvar
echo Enter some text
read myvar
echo '$myvar' now equals $myvar
exit 0
输出结果是:
Hi there
Hi there
$myvar
$myvar
Enter some text
Hello world
$myvar now equals Hello world
1.2、环境变量
脚本在执行的时候,某些变量会根据系统环境设定而进行初始化。这些环境变量通常使用大写,以便和用户自定义的变量区分,后者通常使用小写。下面是一些常用到的环境变量:
环境变量 说明
$HOME 当前用户的登录子目录
$PATH 以冒号分隔的用来分隔搜索命令的子目录清单
$PS1 命令行提示符,通常是“$”字符
$PS2 辅助提示符,用来提示后续输入,通常是“>”字符
$IFS 输入区的分隔符。当shell读取输入数据的时候会把一组字符看做是单词之间的分隔字符,它们通常是空格、制表符和换行符
$0 shell脚本程序的名字
$# 传递到脚本程序的参数个数
$$ 该shell脚本程序的进程ID,脚本程序一般会使用它来创建独一无二的临时文件,比如/tmp/tmpfile_$$
1.3、参数变量
如果你的脚本程序在调用的时候还带有参数,就会产生额外的一些变量。即使你什么参数也没传,上面的“$#”依然存在,只不过值是0罢了。
参数变量 说明
$1,$2... 脚本程序的参数
$* 一个全体参数组成的清单,这是一个单独的变量,各个参数之间用环境变量IFS中的第一个字符分隔开
$@ “$*”的一种变量,它不使用IFS环境变量
下面是使用环境变量和参数变量的一个例子:
#!/bin/sh
salutation="Hello"
echo $salutation
echo "The program $0 is now running"
echo "The second parameter was $2"
echo "The first parameter was $1"
echo "The parameter list was $*"
echo "The user's home directory is $HOME"
echo "Please enter a new greeting"
read salutation
echo $salutation
echo "The script is now complete"
exit 0
输出结果是:
Hello
The program ./try_var is now running
The second parameter was
The first parameter was
The parameter list was
The user's home directory is /home/yea
Please enter a new greeting
Sire
Sire
The script is now complete
2、条件测试
在实际工作中,大多数脚本程序都会大量使用“[ ]”或“test”命令——即shell的布尔判断命令。注意,“[ ]”和要被检查的条件之间留出空格。因为“[”与“test”实际上是一样的,而“test”命令后面要空格,所以“[”后面自然也得有空格了。有关“test”命令的使用,请参考“test”的手册页。我不在此赘述了。
3、控制结构——if、elif、for、while、until、case
shell有一系列控制结构,而且它们同样与其他程序设计语言很相似。就某些结构而言(比如case语句),shell提供了更强大的功能。
为了减小篇幅,并且让大家看起来一目了然,我直接列出各控制结构的语法结构和例程,好在这样写并不会增加理解的难度。
3.1、if语句
if condition
then
statements
else
statements
fi
if语句的例子
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
if [ "$timeofday" = "yes" ]; then
echo "Good morning"
else
echo "Good afternoon"
fi
exit 0
3.2、elif语句
if condition
then
statements
elif condition
then
statements
else
statements
fi
elif语句的例子
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
if [ $timeofday = "yes" ]
then
echo "Good morning"
elif [ $timeofday = "no" ]; then
echo "Good afternoon"
else
echo "Sorry, $timeofday not recognized. Enter yes or no"
exit 1
fi
exit 0
3.3、for语句
for variable in values
do
statements
done
for语句的例子
#!/bin/sh
for foo in bar fud 43
do
echo $foo
done
exit 0
3.4、while语句
假若我们要循环100次,用for的话就得从1一直写到100,这样太累了。它确实没有BASIC里的for方便——FOR I=1 TO 100 STEP 1,但是它有它的优势,这里暂且不谈。
对于上面的情况,我们可以使用while结构和数值替换结合在一起。while的语法结构是
while condition
do
statements
done
看下面的例子
#!/bin/sh
foo=1
while [ "$foo" -le 20 ]
do
echo "Here we go again"
foo=$(($foo+1))
done
exit 0
这里展示了$(command)结构的用法。
3.5、until语句
until和while很类似,只是把条件测试倒过来了。换句话说,就是循环将反复执行直到条件为真是停止,而不是当条件为真时执行。
until的语法为
until condition
do
statements
done
until语句的例子
#!/bin/sh
until who | grep "$1" > /dev/null
do
sleep 60
done
# Now ring the bell and announce the unexpected user.
echo -e \a
echo "***** $1 has just logged in *****"
exit 0
3.6、case语句
case结构比我们前面见过的其他语句都稍微复杂些。它的语法如下所示:
case variable in
patten [| patten] ...) statements;;
patten [| patten] ...) statements;;
...
esac
其实这种语法结构要比我们一般所见到的case结构简洁(比如C/C++的switch~case结构),它可以将多个选项写在一起(就是[]中的可选部分)。需要注意的是,语句结尾要用两个分号“;;”,而不是一个分号。在statements里即可以是一条语句,又可以是数条语句。下面是数条语句的例子:
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
case "$timeofday" in
yes | y | Yes | YES )
echo "Good Morning"
echo "Up bright and early this morning?"
;;
[nN]* )
echo "Good Afternoon"
;;
* )
echo "Sorry, answer not recognised"
echo "Please answer yes or no"
exit 1
;;
esac
exit 0
4、函数
shell允许用户定义函数,以包含某些系统命令,形成执行模块。在shell里定义函数很简单,写出它的名字,加上一对空的“()”,在把有关的语句包含在一对花括号“{}”里,如下所示:
function_name(){
statements
}
shell脚本的执行顺序是自顶向下,因此所有的函数声明都要出现在他执行之前,否则shell将找不到函数。
5、注释
shell脚本用“#”符号注释它之后的语句,直至行尾。这有点像C++里的“//”注释符。但是所有的脚本程序,第一行都由“#!”开头,并指定执行此脚本的程序。这里是“#”另一种功能。仅此而已。
八、实例——Mini Music Jukebox
使用上面的基本元素,我们就可以构件Bash脚本了。理论上,Bash脚本同样可以写出大型、复杂的程序。但是,这样肯定不如用C/C++、Java等编译型语言写的执行效率高。我们学习Bash编程的目的也是为了通过构造shell脚本来减小系统管理的负担,提高管理员的工作效率。所以我们的目标不是大型的程序,而是短小精干的小应用。我用Bash写下了下面的东西,用来播放我的音乐库。
#!/bin/bash
# This is very simple Bash script.It just help us enjoy music.
# Copyright (C) 2003, Free Software Library
# filename: play_music
# To run this script, simply type follow command.
# chmod +x play_music
# ./play_music
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
# This program is distributed in the hopes that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.
# 675 Mass Ave, Cambridge, MA 02139, USA.
# The first thing to do is to ensure that some global variables that we'll be using
# throughout the script are set up. We set the title and track files and a temporary file.
# We also trap Ctrl-C, so our temporary file is removed if the user interrupts the script.
# First,define some globle variables.
choice=""
menu_choice=""
quit=n
# Second,define some functions.
add_mp3(){
echo "Add MP3 files from $HOME/music/mp3 to playlist file \"mp3list\"."
find $HOME/music/mp3/ -iname *.mp3 >> $HOME/music/mp3list
}
add_ogg(){
echo "Add Ogg files from $HOME/music/ogg to playlist file \"ogglist\"."
find $HOME/music/ogg/ -iname *.ogg >> $HOME/music/ogglist
}
play_mp3(){
clear
echo "Playing MP3 files with mpg123."
echo "Control key:"
echo "s=>Stop, p=>Pause, f=>Forward, b=>Backward, q=Quit(Directly)"
echo "Also"
echo "Press Ctrl-C for next song, and press Ctrl-C twice within a short while to quit."
mpg123 -C --list $HOME/music/mp3list
return
}
play_ogg(){
clear
echo "Playing Ogg files with ogg123."
echo "Control key:"
echo "Press Ctrl-C for next song, and press Ctrl-C twice within a short while to quit."
echo
ogg123 $HOME/music/ogg # As the ogg123 do not support playlist, we never use the ogglist file.
return
}
welcome_msg(){
clear
echo
echo "Mini Music Jukwbox--A very simple program written in Bash script."
echo
echo "Before using it, you should at least have the following two program on your system:"
echo "ogg123 - needed for playing Ogg files."
echo "mpg123 - needed for playing MP3 files."
echo
echo "Do you have these software?(y/n):"
read choice
}
set_menu(){
clear
echo "Mini Music Jukwbox."
echo
echo "Options :"
echo
echo " 1) Add MP3 files to playlist"
echo " 2) Add Ogg files to playlist"
echo " 3) Play MP3"
echo " 4) Play Ogg"
echo " q) Quit"
echo
echo "Please enter your choice and then press return"
read menu_choice
}
# Final,the application proper
welcome_msg
if [ "$choice" = "y" ]
then
while [ "$quit" != "y" ]
do
set_menu
case "$menu_choice" in
1) add_mp3;;
2) add_ogg;;
3) play_mp3;;
4) play_ogg;;
q|Q) quit=y;;
*) echo "Sorry, choice not recognized";;
esac
done
else
exit 0
fi
echo "Thanks for using. Bye! :)"
exit 0
在shell模式下运行这个脚本。我在这里只使用了Bash的部分特性,你可以自行修改上面的程序,作为学习Bash的实验。
【GNU/Linux实战手记之Emacs篇 中】到此就结束了。这一篇仅涉及Bash,并未涉及Emacs,所以你完全可以将本篇看做是介绍Bash编程的独立的文章。但是由于我们一直是在Emacs这个环境下编写和调试脚本的,所以我仍将他归到Emacs篇中。在下一篇【GNU/Linux实战手记之Emacs篇 下】中我将介绍如何定制Emacs的IDE环境,使它更好用。我们下篇再见。