Shell 编程实验
了解 Shell 的作用和主要分类。
了解 Bash 的一般语法规则。
练习编写简单的 shell 程序。
1.Shell 变量的定义及其使用。
2.位置参数和环境变量。
3.Shell 中的特殊字符。
4.条件判断结构与循环结构
5.函数的定义和使用
1、LINUX 环境下常用命令和 VI 编辑器的操作。
2、了解 Shell 作用和主要分类等基础知识。
软件:PC 机操作系统 REDHAT ELAS4 环境
Shell 是 Linux 系统中一个重要的层次,它是用户与系统交互作用的界面。在介绍 Linux命令时,Shell 都作为命令解释程序出现:它接收用户打入的命令,进行分析,创建子进程实现命令所规定的功能,等子进程终止工作后,发出提示符。这是 Shell 最常见的使用方式。Shell 除了作为命令解释程序以外,还是一种高级程序设计语言,它有变量,关键字,有各种控制语句,如 if, case, while, for 等语句,有自己的语法结构。利用 Shell 程序设计语言可以编写出功能很强、但代码简单的程序,特别是它把相关的 Linux 命令有机地组合在一起,可大大提高编程的效率,充分利用 Linux 系统的开放性能,设计出适合自己要求的命令。
1.语法练习
(1) Shell 变量
Shell 有两种变量:环境变量和临时变量。在 Shell 脚本中临时变量又分为两类:用户定
义的变量和位置参数。
� 用户定义的变量
用户定义的变量是最普遍的 Shell 变量,变量名是以字母或下化线打头的字母、数字和
下线符序列,并且大小写字母意义不同。变量名的长度不受限制。定义变量并赋值的一般形
式是: 变量名=字符串 例如,
MYFILE=/usr/meng/ff/m1.c
a. 定义并显示变量的值
在程序中使用变量的值时,要在变量名前面加上一个符号“$”
。这个符号告诉 Shell,
要读取该变量的值。
练习 1.1
1.1:
$ dir=/usr/mengqc/file1
$ echo $dir
/usr/mengqc/file1
$ echo dir
dir
$ today=Sunday
$ echo $today $Today
Sunday
$ str="Happy New Year ! "
$ echo "Wish You $str"
Wish You Happy New Year !
b.read 命令
作为交互式输入手段,可以利用 read 命令由标准输入(即键盘)上读取数据,然后赋
给指定的变量。其一般格式是:read 变量 1 [变量 2...]
练习 1.2
1.2:
$ read name -----输入 read 命令
mengqc -----输入 name 的值
$ echo "Your Name is $ name."
Your Name is mengqc -----显示输出的结果
$ read a b c -----read 命令有三个参数
crtvu cn edu -----输入三个字符串,中间以空格隔开
$ echo "Email : $a. $c. $b"
Email : crtvu.edu.cn -----显示输出结果
利用 read 命令可交互式的为变量两赋值。输入数据时,数据间以空格或制表符作为分隔符。
注意以下情况:
*若变量个数与给定数据个数相同,则依次对应赋值,如上面例子所示。
*若变量数少于数据个数,则从左至右依次给变量赋值,而最后一个变量取得所有余下
数据的值。
*若变量个数多余给定数据个数,则从左到右依次给变量赋值,后面的变量没有输入数
据与之对应时,其值就为空串。
� 位置参数
执行 Shell 脚本时可以使用参数。由出现命令行上的位置确定的参数称做位置参数。
在
sh 中总共有十个位置参数,其对应的名称依次是 $0,$1,$2,...$9。其中$0 始终表示命令名或
Shell 脚本名,对于一个命令行,必然有命令名,也就必定有$0;而其它位置参数依据实际
需求,可有可无。
练习 1.3 位置参数的作用。
a.在你的计算机上建立以下三个文件(设建立在目录/usr/username 之下,其中 username
表示你的主目录名)
:
文件 m1.c:
#include <stdio.h>
main( )
{
printf("Begin \n");
}
文件 m2.c:
# include < stdio.h >
main( )
{
printf("OK! \n");
}
文件 ex3:
# ex3: shell script to combine files and count lines
cat $1 $2 $3 $4 $5 $6 $7 $8 $9 | wc -l
# end
b.将 ex3 改为具有执行权限:
$ chmod +x ex3
c.利用 env 命令查看在环境变量 PATH 中是否包含你的当前工作目录
(即/usr/username)
。
如果没有包含在其中,则利用以下语句改变 PATH 的值:
$ PATH=$PATH:/usr/username
d.执行脚本 ex3:
$ ./ex3 m1.c m2.c
10
练习 1.4 利用位置参数给定变量赋值。
a.建立脚本 ex5,如下所示:
n1=$1
n2=$2
n3=$3
cat $n1 $n2 $n3 | wc -l
b.将 ex5 改为具有执行权限:
$ chmod +x ex5
c.执行脚本 ex5:
$ ./ex5 m1.c m2.c
10
� 通配符
通常的通配符有三种:
* 星号,它匹配任意字符的 0 次或多次出现。但注意,文件名前面的圆点(.)和路径
名中的斜线(/)必须显示匹配。
? 问号,它匹配任意一个字符。
[] 一对方括号,其中有一个字符组。其作用是匹配该字符组所限定的任意一个字符。
应该注意:字符 * 和 ?在一对方括号外面是通配符,若出现在其内部,它们就失去通
配符的能力了。
! 叹号,若它紧跟在一对方括号的左方括号[之后,则表示不在一对方括号中所列出的
字符。
� 引号
在 Shell 中引号分为三种:单引号、双引号、和倒引号。
� 双引号
由双引号括起来的字符,除$、倒引号和反斜线(\)仍保留其功能外,其余字符通常作
为普通字符对待。
练习 1.5
a.建立以下文件 ex8:
echo "current directory is`pwd`"
echo "home directory is $ HOME"
echo "file" * . ?
echo " directory ' $ HOME ' "
b.执行 ex8:
$sh ex8
� 单引号
由单引号括起来的字符都作为普通字符出现。
� 倒引号
练习 1.6
1.6:
$ today=`date`
$echo Today is $today
Today is Thu May 04 10 :56 :20 CST 2000
$
又:
$ users=`who | wc -l`
$ echo The number of users is $ users
The number of users is 5
� 反斜线
转义字符,若想在字符串中使用反斜线本身,则必须采用(\\)的形式,其中第一个反
斜线作为转义字符,而把第二个反斜线变为普通字符。
� if 语句
练习 1.7
a.建立脚本 ex9:
echo "The current directory is `pwd`"
if test - f " $1"
then echo " $1 is an ordinary file."
else echo " $ 1 is not anordinary file."
fi
b.执行 ex9:
$sh ex9 ex1
The current directory is /usr/mengqc
ex1 is not anordinary file.
if 语句的 else 部分还可以是 else-if 结构。 (wj14)
if test -f " $1"
then cat $1
else if test -d $1"
then ( cd $1 ;cat * )
else echo "$1 is neither a file nor a directory."
fi
fi
如上例改写成为:
if test -f " $1"
then cat $1
elif test -d " $1"
then ( cd $1 ; cat * )
else echo " $1 is neither afile nor adirectory."
fi
� 测试语句:
有两种常用形式:一种是用 test 命令,如上所示。另一种是用一对方括号将测试条件括
起来。两种形式完全等价。例如,测试位置参数$1 是否是已存在的普通文件,可写成:test
-f " $1",也完全可写成:[ -f $ l ]
在格式上应注意,如果在 test 语句中使用 shell 变量,为表示完整、避免造成歧异起见
最好用双引号将变量括起来。利用一对方括号表示条件测试时,在左方括号[之后、右方括
号]之前各应有空格。
练习 1.8
a.建立脚本文件 ex10
echo "Enter your filename"
read filenane
if [ -f "$filename"]
then cat $filename
else if [ -d "$filename"]
then cd $filename
ls -l *
else echo "$filename:bad filename"
fi
fi
b.执行 ex10
$sh ex10
� while 语句
练习 1.9
a.建立脚本 ex11:
while [ $1 ]
do
if [ -f $1 ]
then echo "display : $1"
cat $1
else echo " $1 is not a file name."
fi
shift
done
b.执行 ex11
练习 1.10
a.建立脚本 ex12:
echo"ley in file->\c"
read filename
echo"key in data:"
while [\n $x]
do
read x
echo $x>>$filename
done
cat $ filename
b.执行 ex12,并分析结果。
� for 语句
练习 1.11
for day in Monday Wednesday Friday Sunday
do
echo $ day
done
例如: (wj20)
for file in m*.c
do
cat $ file | more
done
练习 1.12
a.建立脚本 ex13:
# display files under a given directory
# $1-the nameof the diectory
# $2-the of files
dir=$1;shift
if [ - d $ dir ]
then
cd $dir
for name
do
if [ -f $name ]
then cat $name
echo "End of $ {dir} / $name"
else echo "Invalid file name: $ {dir} / $name"
fi
done
else echo "Bad directory name : $dir"
fi
$
b.执行 ex13,并分析运行结果。
其定义格式如下:
funcname()
{
command
...
command;
}
#分号
定义函数之后,可以在 shell 中对此函数进行调用
如下所示:
iscontinue()
{
while true
do
echo -n "Continue?(Y/N)"
read ANSWER
case $ANSWER in
[Yy])
return 0;;
[Nn])
return 1;;
*) echo "Answer Y or N";;
esac
done
}
这样可以在 shell 编程中调用 iscontinue 确定是否继续执行:
if iscontinue
then
continue
else
break
fi
在一个最简单的例子中,一个 shell 脚本其实就是将一堆系统命令列在一个文件中。好
处就是把所有命令都放在一个脚本中,不用每次都敲它们.这样的话,对于特定的应用来说,
这个脚本就很容易被修改或定制。
在这次练习中,我们编写一个最简单的脚本,其内容是用两条命令清除/var/log/message
和/var/log/wtmp 中的内容。
cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Logs cleaned up."
下面按照 Shell 编程的一些规则进行改良:
在第一行添加一个 Bash 脚本的正确的开头部分,指定解释器为 bash。
使用变量指定/var/log 目录,在后面使用这个变量。
最后用 exit 退出。
使用注释说明各部分的用法。
#!/bin/bash
# 一个 Bash 脚本的正确的开头部分.
# Cleanup, 版本 2
# 当然要使用 root 身份来运行.
# 在此处插入代码,来打印错误消息,并且在不是 root 身份的时候退出.
LOG_DIR=/var/log
# 如果使用变量,当然比把代码写死的好.
cd $LOG_DIR
cat /dev/null > messages
cat /dev/null > wtmp
echo "Logs cleaned up."
exit # 这个命令是一种正确并且合适的退出脚本的方法.
现在,让我们来编写有真正意义的脚本:
添加权限有关语句,判断执行脚本的是否根用户,如果不是则输出出错信息,退出。
添加语句,判断是否有命令行参数,如果有,假设是 n,在后面的清除 log 的时候保留
最后的 n 行;如果没有,设 n=50。
你可以不断地找到新的方法来完善这个脚本,并提高效率。
#!/bin/bash
###############################################################
# 说明:删除 logfile 的脚本
###############################################################
LOG_DIR=/var/log
ROOT_UID=0
# $UID 为 0 的用户才具有根用户的权限
LINES=50
# 默认的保存行数
E_XCD=66
# 不能修改目录,与下面的 E_NOTROOT 相似,用于本脚本退出返回
E_NOTROOT=67
# 非根用户
# 一定要使用根用户来运行
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit $E_NOTROOT
fi
if [ -n "$1" ] # 测试是否有命令行参数(非空).
then
lines=$1
else
lines=$LINES # 如果不在命令行中指定,使用默认
fi
#
#
#
#
#
#
#
#
#
#
可以使用下边的更好方法来检测命令行参数.
其使用了 case 结构
E_WRONGARGS=65
# Non-numerical argument (bad arg format)
case "$1" in
""
) lines=50;;
*[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
*
) lines=$1;;
esac
#
cd $LOG_DIR
if [ `pwd` != "$LOG_DIR" ]
# 也可以用
if [ "$PWD" != "$LOG_DIR" ]
# 查看是否在 /var/log 目录中
then
echo "Can't change to $LOG_DIR."
exit $E_XCD
fi # 在处理 log file 之前,再确认一遍当前目录是否正确.
# 更有效率的做法是:
#
# cd /var/log || {
#
echo "Cannot change to necessary directory." >&2
#
exit $E_XCD;
#}
tail -$lines messages > mesg.temp # 保存 log file 消息的最后部分.
mv mesg.temp messages
# 变为新的 log 目录.
cat /dev/null > wtmp
echo "Logs cleaned up."
exit 0
# 退出之前返回 0,表示成功.