实验二 Shell编程
文章目录
- 实验二 Shell编程
- 一、如果当前目录下有文件f1,但是没有f2,解释命令ls f1 f2 2>ef1 1>&2的运行结果。
- 二、使用for循环语句编写一段B-shell程序,完成显示用户注册目录下的a_sub, b_sub子目录下的所有C程序文件及其目标文件的列表。
- 三、掌握shell程序执行的三种基本方式,注意调试shell程序的命令书写方式有什么不同?
- 四、为便于系统管理员对磁盘分配的管理,请编写一段B-shell程序,当文件系统/home占用空间改变时给出相应的信息提示。要求/home占用量在系统磁盘中为:
- 五、熟悉编程有关基础命令技巧和规则,如变量的命名,引用,位置变量及使用,输出语句及输出格式控制,输入语句和变量存储,从命令输出中提取字段值等。
- 六、编写一段shell程序完成:根据从键盘输入的学生成绩,显示相应的成绩标准(分出不及格、及格、中、良和优秀等)。
- 七、假设score.txt文件中保存了三个班级的学生的某门课程考试成绩,请编写一段shell程序计算每个班级的学生人数与平均分。
一、如果当前目录下有文件f1,但是没有f2,解释命令ls f1 f2 2>ef1 1>&2的运行结果。
PWN@PWN-PC:~/Desktop$ ls f1 f2 2>ef1 1>&2
PWN@PWN-PC:~/Desktop$ cat ef1
ls: 无法访问'f2': 没有那个文件或目录
f1
echo hello 1>&2 | grep aaa
#结果显示:
PWN@PWN-PC:~/Desktop$ echo hello 1>&2 | grep aaa
hello
echo hello 2>&1 | grep aaa
#结果显示:
PWN@PWN-PC:~/Desktop$ echo hello 2>&1 | grep aaa
PWN@PWN-PC:~/Desktop$
echo hello 1>&2 | grep e
#结果显示:
PWN@PWN-PC:~/Desktop$ echo hello 1>&2 | grep e
hello
echo hello 2>&1 | grep e
#结果显示:
PWN@PWN-PC:~/Desktop$ echo hello 2>&1 | grep e
hello
解释:
- 命令的执行分正确输出和错误输出,在使用管道符的时候,管道符只接收管道符前面命令传来d正确输出,这时候就需要用2>&1改变输出类型。
- 管道符只进行传输,不能够存储。所以在第一次2>ef1时就将得到的错误输出重定向给了文件ef1,之后再将1>&2就是把正确输出当作错误输出,然后通过管道2再写入ef1中。
二、使用for循环语句编写一段B-shell程序,完成显示用户注册目录下的a_sub, b_sub子目录下的所有C程序文件及其目标文件的列表。
PWN@PWN-PC:~$ pwd
/home/PWN
PWN@PWN-PC:~$ ls
Desktop Documents Downloads Music Pictures Videos
PWN@PWN-PC:~$ mkdir a_sub b_sub
PWN@PWN-PC:~$ ls
a_sub b_sub Desktop Documents Downloads Music Pictures Videos
PWN@PWN-PC:~$ touch a_sub/file{1..5}.c
PWN@PWN-PC:~$ ls -l a_sub/
总用量 0
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file1.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file2.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file3.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file4.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file5.c
PWN@PWN-PC:~$ touch a_sub/flag{1..3}
PWN@PWN-PC:~$ ls -l a_sub/
总用量 0
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file1.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file2.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file3.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file4.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 file5.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 flag1
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 flag2
-rw-r--r-- 1 PWN PWN 0 10月 8 22:09 flag3
PWN@PWN-PC:~$ touch b_sub/sys_lib{1..5}.c
PWN@PWN-PC:~$ ls -l b_sub/
总用量 0
-rw-r--r-- 1 PWN PWN 0 10月 8 22:10 sys_lib1.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:10 sys_lib2.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:10 sys_lib3.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:10 sys_lib4.c
-rw-r--r-- 1 PWN PWN 0 10月 8 22:10 sys_lib5.c
PWN@PWN-PC:~$
#--------------------------
#!/bin/bash
dirlst="a_sub b_sub "
for i in $dirlst
do
cd $HOME/$i
ls -l *.c
ls -l flag*
done
三、掌握shell程序执行的三种基本方式,注意调试shell程序的命令书写方式有什么不同?
-
脚本文件本身没有权限执行,没有x权限,则使用的方法,或脚本未指定shebang,那么久用指定解释器的方法去执行。
-
含有x权限时,就用./去执行脚本
-
用 . 或者source去执行
-
重定向写入,bash < shell.sh
调试
如何调试Shell脚本
\1) 检查语法错误:
一般来说我们可以通过修改shell脚本的源代码,令其输出相关的调试信息来定位错误,那有没有不修改源代码来调试shell脚本的方法呢?答案就是使用shell的执行选,下面是一些常用选项的用法:
-n 只读取shell脚本,但不实际执行
-x 进入跟踪方式,显示所执行的每一条命令
-c “string” 从strings中读取命令“-n”可用于测试shell脚本是否存在语法错误,但不会实际执行命令。在shell脚本编写完成之后,实际执行之前,首先使用“-n”选项来测试脚本是否存在语法错误是一个很好的习惯。因为某些shell脚本在执行时会对系统环境产生影响,比如生成或移动文件等,如果在实际执行才发现语法错误,您不得不手工做一些系统环境的恢复工作才能继续测试这个脚本。
“-c”选项使shell解释器从一个字符串中而不是从一个文件中读取并执行shell命令。当需要临时测试一小段脚本的执行结果时,可以使用这个选项,如下所示:
sh -c ‘a=1;b=2;let c= a + a+ a+b;echo “c=$c”’“-x"选项可用来跟踪脚本的执行,是调试shell脚本的强有力工具。“-x”选项使shell在执行脚本的过程中把它实际执行的每一个命令行显示出来,并且在行首显示一个”+"号。 "+"号后面显示的是经过了变量替换之后的命令行的内容,有助于分析实际执行的是什么命令。 “-x”选项使用起来简单方便,可以轻松对付大多数的shell调试任务,应把其当作首选的调试手段。
\2) 调试工具-bashdb
使用shell调试器bashdb,这是一个类似于GDB的调试工具,可以完成对shell脚本的断点设置,单步执行,变量观察等许多功能。使用bashdb进行debug的常用命令
1.列出代码和查询代码类:
l 列出当前行以下的10行
- 列出正在执行的代码行的前面10行
. 回到正在执行的代码行
w 列出正在执行的代码行前后的代码
/pat/ 向后搜索pat
?pat?向前搜索pat2.Debug控制类:
h 帮助
help 命令 得到命令的具体信息
q 退出bashdb
x 算数表达式 计算算数表达式的值,并显示出来
!!空格Shell命令 参数 执行shell命令
使用bashdb进行debug的常用命令(cont.)
控制脚本执行类:
n 执行下一条语句,遇到函数,不进入函数里面执行,将函数当作黑盒
s n 单步执行n次,遇到函数进入函数里面
b 行号n 在行号n处设置断点
del 行号n 撤销行号n处的断点
c 行号n 一直执行到行号n处
R 重新启动
Finish 执行到程序最后
cond n expr 条件断点
四、为便于系统管理员对磁盘分配的管理,请编写一段B-shell程序,当文件系统/home占用空间改变时给出相应的信息提示。要求/home占用量在系统磁盘中为:
- 小于50%时,提示“用户文件系统磁盘使用负荷量小”。
- 大于50%,小于90%时,提示“用户文件系统磁盘使用负荷量正常”。
- 大于等于90%时,提示“用户文件系统磁盘使用负荷量偏大。
思路:怎么获取磁盘的空间情况?注意题目是需要知道一个目录的空间占用情况,和磁盘的占用不是一个问题。
单独运行df和du命令,查看用户注册目录的空间使用情况,然后再根据此二命令的输出,决定编程的方法。
#! /bin/bash
hm=`du -s /home|cut -f 1`
#用du查询/home目录大小,参数-s指定目录,cut截取内容
rt=`df /home|grep /|tr -s [:space:]|cut -f 2 -d " "`
#用df获得/home所在磁盘的大小,tr将不一致的间隔缩小成一个间隔,cut截取内容。
let ret=hm*100/rt
#用let运算,计算占比,因为let是整除而且不会显示小数,所以先乘一个100。
#调试:echo "$hm,$rt,$ret"
#调试:ret=30
case $ret in
9[0-9])echo "用户文件系统磁盘使用负荷量偏大";;
[5-8][0-9])echo "用户文件系统磁盘使用负荷量正常";;
[0-4][0-9]|[0-9]) echo "用户文件系统磁盘使用负荷量小";;
esac
五、熟悉编程有关基础命令技巧和规则,如变量的命名,引用,位置变量及使用,输出语句及输出格式控制,输入语句和变量存储,从命令输出中提取字段值等。
变量
-
变量定义与赋值时,变量与值之间不得有空格:var=“abcd”
无需声明类型,且bash默认把所有变量都认为是字符串
-
变量的打印:echo $var 、echo ${var}
-
变量命名规则:
- 见名知意
- 只能包含数字、字母、下划线
- 不能以数字开头
- 不能用标点符号
- 变量名严格区分大小写
-
变量的作用域:切换shell变量会丢失
-
本地变量,只存在于当前shell中,定义shell变量时变量名不需要加$符,本地变量只在用户当前shell生存期中有效
-
环境变量,全局变量,针对当前shell以及其任意子进程,环境变量也分自定义、内置两种环境变量
-
局部变量:针对在shell函数或是shell脚本中定义的
-
位置参数变量:用于shell脚本中传递的参数(相当于命令执行时的参数、路径这种)
-
特殊变量:shell内置的特殊功效变量
$0:获取shell脚本文件名,以及脚本路径
$n:获取shell脚本的第n个参数,n在1~9之间,如$1,$2, 9 , 大于 9 则需要写 9,大于9则需要写 9,大于9则需要写{10},参数空格隔开
$#:获取执行的shell脚本后面的参数总个数
∗ :获取 s h e l l 脚本所有参数,不加引号等同于 *:获取shell脚本所有参数,不加引号等同于 ∗:获取shell脚本所有参数,不加引号等同于@作用,加上引号"$ *"作用,其主要用于接收参数为单个字符串“$1 $2 $3 $4 $5 $6 $7”
$@:不加引号,效果同上,加上引号,是接收所有参数为独立字符串“$1” “$2” “$3” “$4” “$5” “$6” “$7”
-
特殊状态变量
$?:这是一个状态变量,如果上一次的命令执行成功就会是0,错误1-255(错误码)
echo $?:在脚本中用于查看是否执行成功某条指令
$$:取得当前shell脚本的进程号
$!:上一次后台进程的PID
$_:取得上一次执行命令的最后一个参数
-
自定义变量
-
-
引号变量
- 单引号变量,不识别特殊语法
- 双引号变量,能识别特殊符号
- 反引号变量,会将变量值识别为命令,且将命令执行的结果作为变量的值存储起来
调用bsh解释器执行脚本,都会开启一个子shell,因此不保留当前的shell变量;
调用source或者.符号,在当前shell环境下加载脚本,因此保留变量
-
环境变量设置
环境变量一般指的是用export内置命令导出的变量,用于定义shell的运行环境,保证shell命令的正确执行。
shell通过环境变量确定登录的用户名、PATH路径、文件系统等各种应用。
环境变量可以在命令行中临时创建,但是用户退出shell终端,变量就会丢失,如果要永久生效,需要修改环境变量配置文件。
-
用户个人配置文件在:~/.bash_profile、 ~/.bashrc 远程登录用户特有文件
-
全局配置文件/etc/profile 、 /etc/bashrc ,且系统建议最好创建在/etc/profile.d,而非直接修改主文件,修改全局配置文件,影响所有登录系统的用户
优先用户自己配置的环境变量,其次才是全局变量
-
检查系统环境变量的命令
-
set,输出所有变量,包括全局变量、局部变量
set |wc -l: 可以显示总行数
set |grep ^name:找到以name开头的变量
-
env,只显示全局变量
-
declare,输出所有的变量,同set
-
export,显示和设置环境变量值
-
-
撤销环境变量、删除变量或函数:unset 变量名
-
设置只读变量:readonly age=18,设置好之后变量不能再被修改,当shell结束,只读变量才失效。
过滤出环境变量中的所有环境变量的名字:
export | awk -F ‘[:=]’ ‘{print $3}’
-
六、编写一段shell程序完成:根据从键盘输入的学生成绩,显示相应的成绩标准(分出不及格、及格、中、良和优秀等)。
#!/bin/sh
read a
case $a in
9[0-9]|100)echo "优秀";;
8[0-9])echo "良";;
7[0-9])echo "中";;
6[0-9])echo "及格";;
[0-5][0-9])echo "不及格";;
*) echo "Wrong content!";;
esac
七、假设score.txt文件中保存了三个班级的学生的某门课程考试成绩,请编写一段shell程序计算每个班级的学生人数与平均分。
学习读取文件的方法。
如果数据文件的内容是3个班级5门不同课程的内容,程序应该怎么调整?
#!/bin/sh
#这个程序的操作与目标文件中的内容格式关系很大,就是根据文件内容格式来进行的操作
class="计科201 计科202 计科203"
for i in $class
do
total=`grep -F $i score.txt|wc -l`
#以班级为关键词检索出相关行(grep -F $i)并统计行数(wc -l),行数即是人数。
average=`grep -F $i score.txt|awk -F: '{sum+=$3}END{print ":",sum/NR}'`
#在之前检索出来的一个班级的内容中截取第三列所在的内容,并进行累加得到sum,sum再除以内置变量NR(行数)就得到平均值了。
echo "$i班:$total人,平均分$average。"
done
#文本格式
计科201:a:75
计科202:b:76
计科201:c:77
计科203:d:78
计科202:e:79
计科203:f:80
#!/bin/sh
class="计科201 计科202 计科203"
for i in $class
do
total=`grep -F $i score1.txt|wc -l`
echo "$i班:$total人"
for j in 3 4 5 6 7
do
x=`sed -n '1p' score1.txt|awk -F" " '{print $'$j'}'`
#原理和上一个一样,这里就是简单地获取第一行的内容,读取科目,便于简化操作。
average=`grep -F $i score1.txt|awk -F" " '{sum+=$'$j'}END{print ":",sum/NR}'`
echo "$i班$x平均分:$average"
done
done
#文件内容格式
班级 姓名 语文 数学 英语 计算机 网络攻防
计科201 a 110 130 125 99 100
计科201 b 99 101 145 80 75
计科201 c 107 112 126 68 60
计科201 d 113 135 140 95 95
计科202 e 121 140 130 68 52
计科202 f 111 150 30 56 32
计科202 g 141 123 86 23 51
计科202 h 90 25 35 60 75
计科202 i 51 36 85 62 34
计科203 j 134 62 140 55 66
计科203 k 111 112 113 70 71
计科203 l 142 143 144 99 100
计科203 m 150 150 150 100 100
计科203 n 100 100 100 100 100
计科203 o 101 101 101 101 101