1、描述shell程序的运行原理(可附带必要的图形说明);
shell是操作系统的外壳,为用户提供使用操作系统的接口,是命令语言、命令解释程序及程序设计语言的统称;是用户和Linux内核之间的接口程序,如果把Linux内核想象成一个球体的中心,shell就是围绕内核的外层,当从shell或其他程序向Linux传递命令时,内核会做出相应的反应。shell是一个命令语言解释器,它拥有自己内建的shell命令集,shell也能被系统中其他应用程序所调用。用户在提示符下输入的命令都由shell先解释然后传给Linux核心。如图:
2、总结shell编程中所涉及到的所有知识点;
变量:为程序在运行时在内存中申请的空间名字
bash环境,可分为以下变量:
本地变量:其作用域只在当前shell进程;
环境变量:其作用域为当前shell进程及其子进程;
局部变量:某个函数上下文,只存在于某个函数执行过程;
位置参数变量:在脚本中引用传递给脚本的参数;在函数中引用传递给函数的参数;
特殊变量:bash内建的用于保存某些特殊的值的相关变量,如:$?,$*,$@,$#等;
变量类型:
数值和字符类型
变量类型的作用:决定存储空间,运算,定义存储格式
语言对变量类型的支持力度:
强类型:严格区分变量类型,变量在使用前必须先声明变量类型,如C等
弱类型:不严格区分变量类型,一般变量有默认的类型,如bash,默认变量类型为字符型
本地变量:变量名只能包含数字、字母和下划线;且不能以数字开头;
引用变量:$name,${name}
强引用:‘’
弱引用:“”
命令引用:`COMMAND`,$(COMMAND)
声明:declare,let
查看所有变量:set
生命周期:创建到销毁
自动销毁:shell进程终止
手动销毁:unset name
环境变量:
被导出的本地变量:export name[=value]
declare -x name[=value]
查看所有的环境变量:env,printenv,export
销毁:unset name
脚本:实际上是运行一个bash进程,此进程负责从脚本文件中读取一个执行逻辑,而后由bash进程负责解析并运行此逻辑;
启动脚本:
(1)# bash 脚本文件位置
(2)#./脚本文件,通过脚本文件第一行的shebang来确定解释器路径如:#!/bin/bash
bash的常用选项:-n:解析脚本中的语法错误;-x:调试执行脚本
命令状态结果:
bash进程用于追踪执行的命令成功与否的状态:
$?:上一条命令的执行状态结果,0为成功,1-255表示失败
自定义脚本的状态结果:exit [n],在脚本中任何位置执行了exit命令即会终止当前shell进程
条件测试:界定程序执行环境
整数测试:隐含着做数值大小比较,所以不要给变量引用加引用;
$A -gt $B:是否大于;是则为“真”,否则为“假”;
$A -ge $B: 是否大于等于;
$A -lt $B:是否小于;
$A -le $B: 是否小于等于;
$A -eq $B: 是否等于;
$A -ne $B:是否不等于;
字符串测试:ASCII数值越大,字符比较时其值越大;
"$A" > "$B":是否大于;
"$A" < "$B":是否小于;
"$A" == "$B":是否等于;
"$A" != "$B":是否不等于;
-z "$A":是否为空;空则为“真”,否则为“假”
-n "$A":是否不空;不空则“真”,空则为“假”
文件测试:测试文件的存在性以及属性;
-e $file: 是否存在;存在则为“真”,否则为“假”;
-a $file: 同上;
-f $file:文件是否存在且为普通文件;
-d $file:文件是否存在且为目录;
-h $file:是否存在且为符号链接文件;
-L $file: 同上
-b $file:是否存在且为块设备文件;
-c $file:是否存在且为字符设备文件;
-S $file:是否存在且为套接字文件;
-p $file: 是否存在且为管道文件;
-r $file: 当前用户对文件是否拥有读权限;
-w $file:当前用户对文件是否拥有写权限;
-x $file:当前用户对文件是否拥有执行权限;
-u $file:文件是否拥有SUID权限;
-g $file:文件是否拥有SGID权限;
-k $file:文件是否拥有sticky权限;
-O $file: 当前用户是否为指定文件的属主;
-G $file: 当前用户是否为指定文件的属组;
双目操作符:
$file1 -nt $file2: file1是否新于file2, file1的最近一次的修改时间戳是否晚于file2的;
$file1 -ot $file2: file1是否旧于file2, file1的最近一次的修改时间戳是否早于file2的;
$file1 -ef $file2:file1与file2是否指向了同一个inode;测试二者是否为同一个文件的硬链接;
特殊设备:
/dev/null: bit buckets,接受所有的输入数据,并直接丢弃;/dev/zero:吐出一堆0
条件判断
if CONDITION; then
if-true-分支
else
if-false-分支
fi
! CONDITION: 取反
与用户交互:
read [options] VAR...
-p
-t
命令引用:`COMMAND`,$(COMMAND0)
3、总结课程所讲的所有循环语句、条件判断的使用方法及其相关示例
循环语句:
for,while,until
for VAR in LIST;do
循环体
done
LIST生成方法:(1)整数列表
{start..end},$(seq [start [step]] end)
如:for i in {1..10}等于for i in $(seq 1 1 10)
(2)直接给出列表:for i in 1 2 3 4 5 6 7 8 9 10
(3)glob,如:for filename in /var/log/*
(4)命令生成列表,如:for username in $(cut -d: -f1 /etc/passwd)
算术运算:
+, -, *, /, %, **
(1) $[$A+$B]
(2) $(($A+$B))
(3) let VARIABLE=$A+$B
(4) VARIABLE=$(expr $A + $B)
增强型赋值:+=,-=, *=, /=, %=,如:+=相当于
sum=$[$sum+$i]
let sum+=$i
测试表达式:
整数测试:-gt, -lt, -ge, -le, -eq, -ne
字符串测试:==, >, <, !=, -z(字符串长度为0则为真), -n(字符串长度不为0则为真), =~(模式匹配,如:[[ "STRING" =~ PATTERN ]])
组合测试条件:
条件间逻辑运算:
与:多个条件要同时满足;
或:多个条件满足其一即可;
非:对指定的条件取反;
表达式组合:
与:[[ CONDITION1 -a CONDITION2 ]]
或:[[ CONDITION1 -o CONDITION2 ]]
非:[ ! CONDITION ]
命令组合:
与:COMMAND1 && COMMAND2 <-- [ EXPR1 ] && [ EXPR2 ]
或:COMMAND1 || COMMAND2
非:! COMMAND
短路操作符:&&
false && true = false
false && false = false
true && false = true
true && true = true
if COMMAND1; then
COMMAND2
fi
短路操作符:||
true || true = true
true || false = true
false || true = true
false || false = false
if ! COMMAND1; then
COMMAND2
fi
COMMAND1 && COMMAND2 || COMMAND3,相当于
if COMMAND1; then
COMMAND2
else
COMMAND3
fi
多分支if
if CONDITION1; then
if-CONDITION1-true-分支
elif CONDTION2; then
if-CONDITIO2-true-分支
...
else
if-ALL-false-分支
fi
case语句
简洁版多分支if语句;
使用场景:判断某变量的值是否为多种情形中的一种时使用;
语法:
case $VARIABLE in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
PATTERN3)
分支3
;;
...
*)
分支n
;;
esac
PATTERN可使用glob模式的通配符:
*: 任意长度的任意字符;
?: 任意单个字符;
[]: 指定范围内的任意单个字符;
a|b: 多选1;
流程控制
while循环:
while CONDTION; do
循环体
done
进入条件:当CONDITION为“真”;
退出条件:当CONDITION为“假”;
unitl循环:
until CONDITION; do
循环体
循环控制变量的修正表达式
done
进入条件:当CONDITION为“假”时
退出条件:当CONDITION为“真”时
循环控制:
continue [n]:提前结束本轮循环,而直接进入下一轮;
break [n]:提前结束循环;
死循环:
while true; do
循环体
if CONDTION; then
break
fi
done
until false; do
循环体
if CONDITION; then
break
fi
done
while循环的特殊用法:
遍历文件的每一行:while read VARIABLE; do
循环体
done < /PATH/FROM/SOME_FILE,变量为文件中的每一行
for循环的特殊用法:
for ((expr1;expr2;expr3)); do
循环体
done
如:for ((i=1;i<=100;i++))
函数:
function: 功能
把一段具有独立功能代码封装在一起,并给予命名;后续用到时,可直接通过给定函数名来调用整体代码;
函数作用:
代码重用;
模块化编程;
函数的使用方法:
先定义:编写函数代码
后调用:给出函数名,还可按需传递参数
定义方法:
(1) function f_name {
函数体
}
(2) f_name() {
函数体
}
调用函数:
f_name [argu1, argu2, ...]
自定义函数状态返回值:
return [#]
0: 成功
1-255:失败
注意:函数代码执行时,一旦遇到return,函数代码终止运行,函数返回;
模块化编程
功能:把脚本文件中的代码分隔为多段,放在不同的文件中
假设/root/bin/srv目录有两个文件:
(1) 函数文件
(2) 脚本文件
为脚本使用配置文件
一个文件中只定义变量
脚本文件source此变量定义的文件
变量的作用域:
局部变量:
local VARIABLE=value
存活时间:
函数执行开始,至函数返回结束;
4、总结文本处理工具sed及awk的用法;(必须附带示例)
sed:行编辑器,每次读取文件符合模式条件的一行到模式空间,默认不编辑源文件,仅对模式空间的数据做处理,而后将模式空间内容打印至屏幕
sed [options] 'AddressCommand' file ...
-n: 静默模式,不再默认显示模式空间中的内容
-i: 直接修改原文件
-e SCRIPT -e SCRIPT:可以同时执行多个脚本
-f /PATH/TO/SED_SCRIPT
sed -f /path/to/scripts file
-r: 表示使用扩展正则表达式
Address:
1、StartLine,EndLine,比如1,100
$:最后一行
2、/RegExp/,正则表达式来制定模式, 如/^root/
3、/pattern1/,/pattern2/,第一次被pattern1匹配到的行开始,至第一次被pattern2匹配到的行结束,这中间的所有行
4、LineNumber,指定的行
5、StartLine, +N, 从startLine开始,向后的N行;
Command:
d: 删除符合条件的行;删除掉第5行至最后一行的内容
p: 显示符合条件的行;匹配到的行会显示(模式空间和命令处理结果都会显示)2次,如果不需要显示模式空间,需要在前面加-n如:
a \string: 在指定的行后面追加新行,内容为string,如在包含dev的下一行增加aaa
\n:可以用于换行
i \string: 在指定的行前面添加新行,内容为string
r FILE: 将指定的文件的内容添加至符合条件的行处
w FILE: 将地址指定的范围内的行另存至指定的文件中;注意这会覆盖目标文件
s/pattern/string/修饰符: 查找并替换,默认只替换每行中第一次被模式匹配到的字符串加修饰符
g: 全局替换
i: 忽略字符大小写
s///: s###, s@@@,如果匹配中包含“/”,可用“#”或者“@”当分隔符
\(\), \1, \2分组
&: 引用模式匹配整个串
awk:报告生成器,能够将文本中的信息已定义好的格式输出
linux为GNU awk,默认以空格为分割符
使用方法:
#awk [options] 'script' file1 file2...
awk输出:
print:格式,print item1,item2
要点:
1、各项目之间使用逗号隔开,而输出时则以空白字符分隔,-F可以指定输入分割符,-OFS可以指定输出分割符;如图:
2、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
3、print命令后面的item可以省略,此时其功能相当于print $0, 因此,如果想输出空白行,则需要使用print "";
awk变量:
awk内置变量--记录变量:
FS:输入字段分隔符,默认是空白字符;'{FS=""}'
RS:输入行分割符,默认是换行符;
OFS:输出字段分隔符,默认是空白字符;如:
ORS:输出行分割符,默认是换行符;如:
awk内置变量--数据变量:
NR:awk所处理的记录数,如果有多个文件,则会把多个文件处理行统一技计数;
NF:统计当前处理行的字段总数
FNR:当前文件中正在处理的行数,如:
用户自定义变量和赋值变量,如:
awk -v a=“test”
awk '{a="test"}'
printf命令的使用格式:
printf:不会自动打印换行符,与print相比需要指定格式(format)
printf format item1,item2.....
format格式的指示符都%开头,后跟一个字符:
%c: 显示第一个参数的第一个字母;
%d, %i: 十进制整数;
%e, %E: 科学计数法显示数值;
%f: 显示浮点数;
%g, %G: 以科学计数法格式或浮点数格式显示数值;
%s: 显示字符串;
%u: 显示无符号整数;
%%: 显示%自身;
修饰符:
#:显示宽度
-:左对齐,默认为靠右对对齐
+:显示数值的符号
.#: 取值精度
如:
awk的输出重定向
awk操作符:
算数操作符:+,-,*,/,等
字符串操作符:用于实现字符串连接
赋值操作符:=,+=,-=,--,++等
布尔值:在awk中任何非0或者非空字符串都为真,其余为假
比较操作符:<,<=,>,>=,==,!=,~(模式匹配),!~,subscript in array在某个数组中是否存在某个元素
逻辑操作符:&&,||
条件表达式:
selector?if-true-exp:if-false-exp,相当于以下语句
if selector;then
if-true-exp
else
if-false-exp
fi
如:比较两个数字的大小
awk的模式:awk ‘programe’file1 file2 ..., 其中programe为 pattern { action }
常见的模式类型:
正则表达式:格式为/regular expression/,如:awk -F: '/^r/{print $0}' /etc/passwd
表达式:其值为非0或者非空是满足条件,如:
awk -F: '$3>1013{print $0}' /etc/passwd
指定的匹配范围:ranges,格式为pat1,pat2,如:
awk -F: '/^r/,/^l/{print $0}' /etc/passwd
BEGIN/END: 特殊模式,仅在awk命令的program运行之前(BEGIN)或运行之后(END)执行一次,如:awk -F: END'{print NF}' /etc/passwd,只显示最后一次的结果
如增加表头和表尾:
空模式:匹配任意输入行
常见的Action
表达式
控制语句:
if-else格式:
if (condition) {then body} else {else body}
awk -F: '{if ($3>=500) {print $1,"is a common user"} else {print $1, "is an admin or system user"}}' /etc/passwd
while格式:
while (condition) {while body}
do-while格式:do {do-while body} while (condition)
for格式:
for (variable assignment; condition; iteration process) {for body}
awk '{for (i=1;i<=NF;i+=2){printf "%s ",$i};print ""}' /etc/inittab
for循环可用来遍历数组元素:for (i in array) {for body}
case:
switch (expression) {case VALUE or /RGEEXP/: statement1;... default: stementN}
循环控制:continue,break
next:
提前结束对本行的处理进而提前进入下一行的处理;
# awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
# awk -F: '{if(NR%2==0) next; print NR,$1}' /etc/passwd
数组:
array[index-expression]
index-expression: 可以使用任意字符串; 如果某数组元素事先不存在,那么在引用时,awk会自动创建此元素并将其初始化为空串;因此,要判断某数组是否存在某元素,必须使用“index in array”这种格式
要遍历数组中的每一个元素,需要使用如下特殊结构:
for (var in array) {for body}, 其var会遍历array的索引;
如统计当前系统连接状态数量:
常用内置函数:
split(string,array[,fieldsep[,seps]]):
功能:将string表示的字符串以fieldsep为分隔符进行切片,并切片后的结果保存至array为名的数组中;数组下标从1开始;
此函数有返回值,返回值为切片后的元素的个数
# netstat -tn | awk '/^tcp/{lens=split($5,client,":");ip[client[1]]++}END{for (i in ip) print i,ip[i]}'
length(string)
功能:返回给定字串的长度
substr(string,start[,length])
功能:从string中取子串,从start为起始位置为取length长度的子串;
5、写一个脚本:如果某路径不存在,则将其创建为目录;否则显示其存在,并显示内容类型;(不要怀疑,就是这么简单)
脚本:if [ ! -d $1 ];then
echo "$1 is not exist,creat directory $1"
mkdir $1
echo "done"
else
echo "directory is exist"
ls -l $1
fi
6、写一个脚本,完成如下功能;判断给定的两个数值,孰大孰小;给定数值的方法:脚本参数,命令交互;(使用read,依然如此简单)
脚本参数:if [ $# -gt 0 ];then
if [ $1 -gt $2 ];then
echo "$1 is greater than $2"
elif [ $1 -lt $2 ];then
echo "$2 is greater than $1"
else
echo "two parameters have the same value"
fi
else
echo "please input two parameters"
exit 8
fi
命令交互:tip="Please input two number:"
read -p "$tip" a b
(( $a )) && (( $b )) 2> /dev/null
if [ $? = 0 ];then
if [ $a -gt $b ];then
echo "a is greater than b"
elif [ $a -lt $b ];then
echo "b is greater than a"
else
echo "two parameters have the same value"
fi
else
echo "please input two interger number"
fi
7、求100以内所有奇数之和(至少用3种方法)
for循环:
sum=0
for i in $(seq 1 2 100);do
sum=$[$sum+$i]
done
echo $sum
while循环:
sum=0
j=1
while [ $j -lt 100 ];do
sum=$(($sum+$j))
j=$(($j+2))
done
echo $sum
until循环:
sum=0
k=1
until [ $k -gt 100 ];do
sum=$(($sum+$k))
k=$(($k+2))
done
echo $sum
8、写一个脚本实现如下功能:
(1) 传递两个文本文件路径给脚本;
(2) 显示两个文件中空白行数较多的文件及其空白行的个数;
(3) 显示两个文件中总行数较多的文件及其总行数;
9、写一个脚本
(1) 提示用户输入一个字符串;
(2) 判断:
如果输入的是quit,则退出脚本;
否则,则显示其输入的字符串内容;
10、写一个脚本,打印2^n表;n等于一个用户输入的值;
11、写一个脚本,写这么几个函数:函数1、实现给定的两个数值的之和;函数2、取给定两个数值的最大公约数;函数3、取给定两个数值的最小公倍数;关于函数的选定、两个数值的大小都将通过交互式输入来提供。