SHANGHAI UNIVERSITY
操作系统(一)实验报告
组 号 | 第 4 组 |
---|---|
学号姓名 | 20120889 |
实验序号 | 实验六 |
日期 | 2022年9 月 10 日 |
一、 实验目的与要求
实验目的:
- 掌握vi 的三种工作方式,熟悉vi 编辑程序的使用。
- 学习Shell 程序设计方法。掌握编程要领。
二、实验环境
VMware+Linux发行版 Ubuntu 20.04版
三、实验内容及其设计与实现
3.1 按本《实验指导》第三部分的内容。熟悉vi 的三种工作方式。熟悉使用各种编辑功能。
思考:试一试vi 的三种工作方式各用在何时?用什么命令进入插入方式?怎样退出插入方式?文件怎样存盘?注意存盘后的提示信息
答:
三种工作方式分别为:插入方式、转义命令方式和末行命令方式。进入vim时默认进转义模式:
在转义命令方式下输入i进入插入模式,在插入模式下输入ESC退出:
在末行命令模式下输入w回车可存盘,存盘提示信息如下:
3.2 创建和执行Shell 程序
用前面介绍的Vi 或其他文本编辑器编写Shell 程序,并将文件以文本文件方式保存在
相应的目录中。
答:首先使用vim创建shdemo.h:
然后在vim编辑器下输入下列程序段:
string1="Hello world!"
echo $string1
编辑完毕后保存退出,赋予该文件755的权限,然后执行,可看到如下结果:
3.3用vi 编写《实验指导》"第四部分Shell 程序设计"中的例1(假设文件名为prog1.h),练习内部变量和位置参数的用法。
答:首先查看例一代码:
#Name display program
if [ $# == 0 ]
then
echo "Name not provided"
else
echo "Your name is " $1
fi
直接使用上述代码,跑出来的结果出错,无论键入是否为空都输出your name is...。查看源代码,可以明显看到他的错误为:
- 没有使用带空格的=作为等于判断符,而是使用==,这在高级语言是成立的,但shell不支持该语法。
修改后为:$# = 0
.用vim创建文件prog1,输入该代码段并赋予权限755,运行:
可以看到测试结果符合要求。
3.4进一步修改程序prog1.h,要求显示参数个数、程序名字,并逐个显示参数。
答:显示参数个数可以直接使用$#显示。程序名字也容易显示。关键点在于如何逐个显示所有参数。查看指导书关于for循环的使用,了解到其中for curvar后不加列表可以直接默认是从参数列表中依次取参数赋值给curvar,恰好适合题目要求,因此编写程序如下:
echo "This is prog1.h\n"
cnt=1
if[$#=0]
then
echo "0 argument.\n"
else
echo $"arguments:\n"
for curvar
do
echo $curvar "\n"
done
fi
将该程序通过vim键入prog1.h,权限之前已修改,直接运行,首先不输入参数,可见程序提示0 argument。之后再次运行并给予参数列表,参数之间以空格分开,可以看到程序正确辨别参数个数并且依次输出。
3.5修改例1程序(即上面的 prog1.h),用read命令接受键盘输入。若没有输入显示第一种提示,否则第二种提示。
答: 查询资料,得知read命令可跟多种参数,其中read -p+提示信息的组合方式可以在接收输入的同时向用户提示信息,比较便捷。所以我们利用该指令可以编写如下指令:
read -p "Please input a sentence:" str
if [ "$str" = "" ]
then
echo -e "NO Input. \n"
else
echo "Got the sentence " $str
fi
其中echo -e是为了后面\n可以识别为换行而加的。将该程序通过vim键入prog1.h,权限之前已修改,直接运行,首先不输入参数,则程序提示No Input。之后再次运行,键入hey,可以看到程序输出另一种提示,之后又回显了用户的输入。
3.6用vi 编写《实验指导》"第四部分 Shell 程序设计"中的例2、例3,练习字符串比较运算符、数据比较运算符和文件运算符的用法,观察运行结果。
1. 首先编写例二程序:
查看实验指导书的源程序代码,可以看到明显错误,分别为:
-
变量前面不加$
-
注释前边不加#
-
引号均为中文引号
将其修改如下:
string1="The First one"
string2="The second one"
if [ $string1 = $string2 ]
then
echo "string1 equal to string2"
else
echo "string1 not equal to string2"
fi
if [ $string1 ]
then
echo "string1 is not empty"
else
echo "string1 is empty"
fi
if [ -n "$string2" ]
then
echo "string2 has a length greater than zero"
else
echo "string2 has a length equal to zero"
fi
该程序通过vim键入compare.h,修改权限为755,直接运行,可以看到输出正确结果。
2. 编写例三程序:
查看实验指导书的源程序代码,可以看到明显错误,分别为:
-
filea和cppdir均是文件名,应为字符串类型,使用时应加""。
-
引号均为中文引号
将其修改如下:
if [ -d "cppdir" ]
then
echo "cppdir is a directory"
else
ecgo "cppdir is not a directory"
fi
if [ -f "filea" ]
then
echo "filea is a regular file"
else
echo "filea is not a regular file"
fi
if [ -r "filea" ]
then
echo "filea has read permissione"
else
echo "filea dose not have read permissione"
fi
if [ -w "filea" ]
then
echo "filea has write permissione"
else
echo "filea dose not have write permissione"
fi
if [ -x "cppdir" ]
then
echo "cppdir has execute permissione"
else
echo "cppdir dose not have execute permissione"
fi
编写完成后,首先将cppdir/fiea的权限均设为777,为comepare.h赋予755的权限后执行,可以看到正常输出。然后将filea权限改为111(所有用户不可读不可写可执行),重复执行程序,可以看到filea已经没有读写权限了。
我们再修改cppdir的权限为111,重复执行该程序,可以看到输出没有变化:
继续修改cppdir的权限为444,使得cppdir不再能被执行,重复执行该程序,可以看到确实失去执行权限:
3.7 修改例2程序,使在程序运行中能随机输入字符串,然后进行字符串比较。
首先分析要求,随即读入字符串可以使用read指令,字符串比较本身就是compare.h的功能,因此稍加修改,在代码前新加两行即可:
read -p "Please input the first sentence:" string1
read -p "Please input the second sentence:" string2
下面进行测试:
-
str1str2null:可以看到判断出了他们相等,并且str1是空的,str2长度为0:
-
str1==str2!=null:当str1=str2=hello时,输出相等,并且均非空:
-
str1!=str2,str1==null:
-
str1!=str2,且str1!=null,str2!=null:
可以看到各种情况下的输出是正确的。
3.8修改例3程序,使在程序运行中能随机输入文件名,然后进行文件属性判断。
和上一题类似,我们可以使用read完成随即输入文件名的任务,而文件属性判断之前已经做过,可以直接使用。但是其中有一些区别,即我们读取到的文件名字符串被放入了一个变量中,不能像之前直接用字符串名称匹配,而是使用$+变量名的方式。
read -p "please input the name of your file/dir:" filea
if [ -d "$filea" ]
then
echo "$filea is a directory"
else
echo "$filea is not a directory"
fi
if [ -f "$filea" ]
then
echo "$filea is a regular file"
else
echo "$filea is not a regular file"
fi
if [ -r "$filea" ]
then
echo "$filea has read permissione"
else
echo "$filea dose not have read permissione"
fi
if [ -w "$filea" ]
then
echo "$filea has write permissione"
else
echo "$filea dose not have write permissione"
fi
if [ -x "$filea" ]
then
echo "$filea has execute permissione"
else
echo "$filea dose not have execute permissione"
fi
保存退出后,先测试filea,将其权限设置为777,然后运行compare2.h,观察结果:检测到这个文件名为filea,不是一个目录而是一个普通文件;具有执行权限、读写权限。
为了对比,将其权限设为111,再次运行,可以看出此时filea已经没有读写权限了,只有执行权限。
接下来测试一个目录cppdir,首先将其权限设置为666(可读可写不可执行),然后运行compare2.h,观察结果:检测到这个目录名为cppdir,是一个目录而不是一个普通文件;具有读写权限,但是没有执行权限。
为了对比,将其权限设为444(只可读),再次运行,可以看出cppdir只有可读的权限了。
3.9用vi 编写《实验指导》“第四部分 Shell 程序设计”中的例4、例5、例6、例7,掌握控制语句的用法,观察运行结果。
-
例4:下面程序将当前目录中的文件(用命令 ls 列出),复制到 backup 子目录中建立备份。首先查看实验指导书例程,学习语法:
- `也叫反括号。反引号括起来的字符串作为命令执行,并将结果放入变量。
- cp source destination可以将source程序复制到destination中。cp后还可以跟指令,如-r可以将子目录复制等。
将实验指导书源程序摘录,并对其中错误的符号修改,且将if后的then补齐,运行后仍报错:
查看源代码,发现if[]必须和内部判断式之间有一个空格分隔,添加空格后,首先添加一个空文件夹backup。然后运行,仍报错。仔细查看后发现例题源程序竟然使用了非规范的符号“-”,对其进行进行修改后不再报错。
完整代码如下:
for filename in `ls` do cp $filename ./backup/$filename if [ $? -ne 0 ] then echo "copy $filename failed" fi done
修改后运行结果如下:其中的报错分为两种:(1)Permisson Denied.这是因为我们之前把filea设置为不可读文件,因此无法复制;(2)文件夹。由于我们是移动的文件,但是ls指令会把所以本目录下文件与子目录一并读取,因此每次碰到文件夹就会报failed。下图为终端运行结果和运行结束后backup文件夹中的内容。
-
例5.十个偶数的相加。该段程序主要使用下列知识点:
- -lt: shell的数字比较操作符,意为小于。这里是在while循环中比较loopcnt是否小于十,如果大于等于则退出。
- while循环,与高级语言类似,只是也需要done结尾。
对源程序的错误符号修改后运行,果不其然报错了:
根据报错提示回到第3行,发现while和[之间没有空格,添加空格后报错变为:
最后查询网上资料,发现:
- shell语法在*前必须加\,这是因为*在shell中可表示为任意一个字符,或者字符串。
- 使用expr表示后面的语句为表达式时,当计算$loopcountX2时,其优先级高于加法,所以不可以使用()。
在修改完之后,程序终于成功输出了正确结果,我不免感叹一段这么简单的代码竟然会放进这么多错误语法!
以下为正确代码:
loopcount=0 result=0 while [ $loopcount -lt 10 ] do loopcount=`expr $loopcount + 1` result=`expr $result + $loopcount \* 2 ` done echo "result is $result"
-
例6.进行 10 个偶数的相加:浏览实验指导书代码,这段代码功能和上述一样,结构也十分类似,唯一不同是换作了until。until 语句可用来执行一系列命令,直到所指定的条件判定为真时才终止。因此我们需要放入相反的截止条件,也就是当loopcount大于等于10,则until中止。
由于有了上题的改错经验,这次我们直接改掉所有的错误:
loopcount=0 result=0 until [ $loopcount -ge 10 ] do loopcount=`expr $loopcount + 1` result=`expr $result + $loopcount \* 2 ` done echo "result is $result"
4.例7.执行以下语句时系统生成一个显示数字的菜单,用户选择 1,则 item 中含有 continue;选择 2,则 item 中含有 finish。
学习select的语法结构,可知select做的是从itemlist中按序号选择对应的item赋给变量,然后配合do和done实现循环操作。
实验指导书代码包含以下错误:
- item列表之间各item没有以逗号分隔
- item列表元素没有加“”,变成了变量
- 使用中文引号
- if后面没有跟then
select item in "continue" "finish" do if [ "$item" = "finish" ] then break fi done
3.10用vi 编写《实验指导》“第四部分 Shell 程序设计”中的例8 及例9掌握条件语句的用法,函数的用法,观察运行结果。
答:
- 例8:首先学习case的语法结构:与高级语言的switch case类似,是通过)内的参数与case item里的item匹配,来跳到对应的case里。
case $1 in 01|1) echo “Month is January”;; 02|2) echo “Month is February”;; 03|3) echo “Month is March”;; 04|4) echo “Month is April”;; 05|5) echo “Month is May”;; 06|6) echo “Month is June”;; 07|7) echo “Month is July”;; 08|8) echo “Month is August”;; 09|9) echo “Month is September”;; 010|10) echo “Month is October”;; 011|11) echo “Month is November”;; 012|12) echo “Month is December”;; *) echo “Invalid parameter”;; esac
运行结果如下:
-
例9:使用函数调用的方法,在shell脚本文件中封装一个函数供用户调用。函数内容就是之前的脚本程序,在程序外则是脚本,直接调用两次该函数并传参即可。
displaymonth ( ) { #定义函数 case $1 in 01|1) echo “Month is January”;; 02|2) echo “Month is February”;; 03|3) echo “Month is March”;; 04|4) echo “Month is April”;; 05|5) echo “Month is May”;; 06|6) echo “Month is June”;; 07|7) echo “Month is July”;; 08|8) echo “Month is August”;; 09|9) echo “Month is September”;; 010|10) echo “Month is October”;; 011|11) echo “Month is November”;; 012|12) echo “Month is December”;; *) echo “Invalid parameter”;; esac } displaymonth 8 #调用函数 displaymonth 12
3.11编程,在屏幕上显示用户主目录名(HOME)、命令搜索路径(PATH),并显示由位置参数指定的文件的类型和操作权限。
答:首先,shell程序中可以使用
echo ${HOME}
显示主目录名,使用echo $PATH
搜索路径,并使用我们之前学习的位置参数显示文件类型和操作权限,我们可编制程序如下:read -p "Input The file name you want to search:" filea echo ${HOME} echo $PATH if [ -d "$filea" ] then echo "$filea is a directory" else echo "$filea is not a directory" fi if [ -f "$filea" ] then echo "$filea is a regular file" else echo "$filea is not a regular file" fi if [ -r "$filea" ] then echo "$filea has read permissione" else echo "$filea dose not have read permissione" fi if [ -w "$filea" ] then echo "$filea has write permissione" else echo "$filea dose not have write permissione" fi if [ -x "$filea" ] then echo "$filea has execute permissione" else echo "$filea dose not have execute permissione" fi
运行结果为:首先搜索filea:
再修改权限为777,再次运行:
3.11思考:
① 做个批处理程序,体会一下批处理概念。
② 做个菜单,显示系统环境参数。将此程序设置为人人可用。
-
批处理,也被称为批处理程序或脚本,可以简化日常或重复性任务。.bat是dos下的批处理文件,而另一个是linux下的批处理文件。本质就是shell命令集合。所以我们只要编写一个shell脚本,完成一个本需要手动完成的任务即可。因此我们选择编写批处理程序,把hello输入到文本文件text.txt中。
首先创建text.txt和hello.h,写入下列程序到hello.h中:
read -p "iput your name:" name echo "Hello,${name}" > text.txt
2.显示系统环境参数的菜单:为了显示系统的环境信息,学习以下命令的使用方法与含义:
-
|:管道。我们后续需要对很多指令的输出进行加工,过滤有用信息,于是就可将命令的输出通过管道给到下一个命令的输入,这样就可进行过滤操作。
-
free - 显示系统已用及空余物理内存量、交换分区使用情况。其命令形式为 free + options。 -m表示显示单位为MB。
-
grep:Global Regular Expression Print,表示全局正则表达式版本,用于搜索指定的字符串。我们的很多全局信息指令包含许多行信息,如果只需要截取我们需要的信息,利用grep是方便的选择。例如如果使用该命令
echo "CPU:`cat /proc/cpuinfo`"
可以输出所有关于CPU的信息,但是其信息非常多,有些并不需要输出,可以看到运行结果如下:在这种情况下我们可以进行筛选,比如我们想要CPU型号和主频,那么我们只需要使用grep筛选即可:
echo "CPU:`cat /proc/cpuinfo|grep "model name"`" echo "CPU MHZ:`cat /proc/cpuinfo|grep "cpu MHz"`"
运行结果就变得十分简洁了:
-
cut:剪切,是以每一行为一个处理对象的。使用cut -d可以指定切割符,配合-f 可以选择切割后输出哪一列。再看刚刚我们的运行结果,会发现我们在提示信息“CPU MHZ:”之后又跟着系统自动的提示信息cpu MHz了,这样看着比较冗杂,我们就可以用:作为分隔符进行cut,只保留:后面的内容,也就是第二列。故程序可改为:
echo "CPU:`cat /proc/cpuinfo|grep "model name"|cut -d: -f2 `" echo "CPU MHZ:`cat /proc/cpuinfo|grep "cpu MHz"|cut -d: -f2`"
再次运行,可以看到这时输出只保留了我们自己编写的提示信息,更加整齐了。
-
head:该命令用于显示文件头部内容,默认执行
head
命令会输出文件开头的 10 行。我们会看上面的代码,会产生疑问:为什么每个信息都输出了两边呢?于是回看echo "CPU:`cat /proc/cpuinfo`"
的输出信息,发现竟然所有信息都输出了两遍!查看网络资料才明白,这是因为CPU有两个处理核时,每一个核都会输出一遍信息!因此我们选择使用head,head命令是用来规定显示多少行的,之前我们已经对列进行了划分,现在只需输出第一行,不输出第二行即可:echo "CPU:`cat /proc/cpuinfo|grep "model name"|cut -d: -f2 |head -1`" echo "CPU MHZ:`cat /proc/cpuinfo|grep "cpu MHz"|cut -d: -f2|head -1`"
再次运行,可以看到输出简洁美观:
利用上述命令,我们可以写以下程序,获取想知道的环境变量:
#!/usr/bin/env bash echo "Hostname:" `hostname` echo "CPU:`cat /proc/cpuinfo|grep "model name"|cut -d: -f2 |head -1`" echo "CPU MHZ:`cat /proc/cpuinfo|grep "cpu MHz"|cut -d: -f2|head -1`" echo "Core version: `uname -r`" echo "Memory Size: `free -m |grep Mem|tr -s " "|cut -d" " -f2` MB" echo "Pv4 address: $(ifconfig ens33|grep netmask|tr -s " "|cut -d" " -f3)"
给予其777的权限,因此所有人均可以使用该程序查看系统参数。运行程序,结果如下:
-
4.讨论题
4.1Linux 的Shell 有什么特点?
- shell是一种解释型语言,之前我们编写的所有shell程序,在编辑保存后就可以直接运行,无需编译。
- 可以在编写shell脚本时直接使用现有的所有命令,无需自定义函数(当然shell允许自定义函数)。
- shell编写的程序可以直接在后台运行。
- 可以使用管道和重定向功能。
4.2怎样进行Shell编程?如何运行?有什么条件?
- 用户直接创建一个文件,使用符合shell编程语法的语句编写即可。
- 对于shell脚本的运行,其必要条件是需要有执行权限。用户随意创建的文件不具有执行权限,配合chmod指令可赋予执行权限。在保存退出后直接使用sh 或./+文件名,即可运行shell脚本
4.3 vi 编辑程序有几种工作方式?查找有关的详细资料,熟练掌握屏幕编辑方式、转移命令方式以及末行命令的操作。学习搜索、替换字符、字和行,行的复制、移动,以及在vi中执行Shell命令的方式。
-
关于vim的工作方式和切换操作,在之前实验时已经做过介绍。
-
在vim中可使用
/
和?
快速搜索文本。想要向前搜索按/
,想要向后搜索按?
,按n
来搜索下一个出现的匹配结果,按大写的N
反向搜索下一个出现的匹配结果。还可以搜索整词,将光标移动到这个词语上,然后按*
向前搜索,或者#
向后搜索。 -
vim中替换操作:使用
s
(substitute)命令用来查找和替换字符串,其作用范围分为当前行、全文、选区等等。其语法为:{作用范围}s/{目标}/{替换}/{替换标志}
.例如:%s/filea/file/g
会在全局范围(%
)查找filea
并替换为file
,所有出现都会被替换(g
). -
行的复制:在命令模式下,在要复制行的位置按“yy”,复制当前行;然后再光标的行按“p”,粘贴到下一行,原来的往下顺移。如果想复制多行,就使用nyy(比如3yy,复制3行)。
-
对于行的移动,有下面的指令:
0 移动到行首 $ 移动到行末 + 移动到下一行开头 - 移动到上一行开头
-
在vim中执行命令:在末行命令模式下,输入:!command,就可以不退出vim,并执行shell命令command,将命令输出显示在vim的命令区域,不会改变当前编辑的文件的内容。此外,:r !command指令可以将shell命令command的结果插入到当前行的下一行。甚至还可以用:起始行号,结束行号 w !command的方式,将vim中起始~结束行的所有代码当作命令执行。
4.4 编写一个具有以下功能的Shell程序。
(1) 把当前目录下的文件目录信息输出到文件 filedir.txt 中;
(2) 在当前目录下建立一个子目录,目录名为 testdir2 ;
(3) 把当前目录下的所有扩展名为 c 的文件以原文件名复制到子目录testdir2中;
(4) 把子目录中的所有文件的存取权限改为不可读。(提示:用 for 循环控制语句实
现,循环的控制列表用 ’ls’ 产生。)
(5) 在把子目录 testdir2 中所有文件的目录信息追加到文件 filedir.txt 中;
(6) 把你的用户信息追加到文件 filedir.txt 中;
(7) 分屏显示文件 filedir.txt
答:
- 文件目录信息输出:首先,我们可以使用
ls
获取目录下所有文件名。接下来要做的就是输出到文件。这个可以使用下方命令直接完成:
`ls -l > filedir.txt`
- 建立子目录testdir2:使用mkdir指令:
`mkdir testdir2`
-
看到题目,回忆起之前的cp指令,使用其完成文件的复制操作。要把当前目录下的所有扩展名为 c 的文件以原文件名复制到子目录testdir2中,我们需要使用find过滤出所有后缀名是.c的文件,可以用正则表达式的*.c来做:
for item in `find . -maxdepth 1 -name "*.c"` do cp $item ./testdir2/$item if [ $? -ne 0 ] then echo "copy $item failed" fi done
-
把子目录中的所有文件的存取权限改为不可读:我们使用ls命令取出所有子目录testdir2下的文件,然后在for循环内使用chmod对其进行权限的修改。注意:ls取出来的只是文件名,而不包括路径。因此在修改权限时,需要先进入指定的子目录再改!
for item in `ls ./testdir2` do chmod 333 ./testdir2/$item if [ $? -ne 0 ] then echo "chmod $item failed" fi done
可以看到确实完成了权限修改:
-
子目录 testdir2 中所有文件的目录信息追加到文件 filedir.txt:本题和第一题的最大区别在于追加。追加需要使用>>,这样新输入的信息不会覆盖原有的。
`ls ./testdir2 -l >> filedir.txt`
运行后我们可以看到,确实在原有的文件下追加了新的testdir2 中所有文件的目录信息,并且观察权限可知,它们的确都没有可读权限了。
-
把你的用户信息追加到文件 filedir.txt 中:
可以直接使用who获取用户信息,然后追加即可:
` who >> filedir.txt `
-
分屏显示文件 filedir.txt:使用more命令可以进行分屏显示。
more filedir.txt
四、收获与体会
说明:撰写完成该实验后的收获和体会。
本次实验共分为两个方面,主要是对vim编辑器的各项编辑功能和shell脚本主要命令、语法的学习实践。
在学习vim之前就使用vim在linux下创建、编辑过文件,但也只知道可以用i插入,:wq可以保存退出而已。通过这次试验,学习到了更多的vim指令,更了解了vim的三个工作模式,对于掌握不同模式下的vim操作十分有用处。此外,我还学到了在vim编辑器中如何查找、替换字符,如何添加行和转换行的位置等等;最有意思的是我了解到了vim可以直接在末行工作模式下使用command实现直接运行shell指令的操作,还可指定vim中部分行作为命令程序,用shell动态运行,将结果直接打印在vim文档里。这有利于我对解释型语言特点的了解。
此外,我还学习了shell脚本的编写。之前的实验中我们也经常和linux环境下的shell打交道,在我眼里shell就和windows下的cmd一样可以直接键入命令。但是在本节实验中,我又认识到了shell作为编程语言的另一面,即其可以直接存在于一个文件中,作为批处理直接提交系统运行;也可以在shell编程文件中调用shell命令,对返回值再加处理;最后,shell的语法也让我明白shell同时自成一门语言,而不仅仅是几条即时执行的命令。拜shell语言的语法特性和实验指导书中精心编写的例题源程序所赐,我也对shell语言与C/C++等高级语言语法的区别有了十分深刻的认识。
标签:shell,权限,filea,echo,vim,编辑器,Shell,fi,Vim From: https://www.cnblogs.com/czy-blogs/p/16878715.html