Linux系统编程第十章学习笔记
前言
本章讨论了sh编程,阐述了sh脚本和不同版本的sh;比较了sh脚本与C程序,并指出了解释语言和编译语言的区别;详细说明了如何编写sh脚本,包括sh变量、sh语句、sh内置命令、常规系统命令和命令替换;解释了sh控制语句,其中包括测试条件、for 循环、while循环、do-until 循环、case 语句等,并示范了它们的用法;说明了如何编写sh函数以及使用参数调用sh函数;还举例说明了sh脚本的广泛应用,包括Linux系统的安装、初始化和管理。
1.sh脚本
sh脚本是一个包含sh语句的文本文件,命令解释程序sh要执行该语句。例如,我们可以创建一个文本文件 mysh,如下
#! /bin/bash
# comment line
echo hello
以下为实操
打开终端
输入指令
emacs mysh//创建mysh文件,填充内容如下图
chmod +x mysh//赋予mysh可执行权限
./mysh//执行mysh
2.sh脚本与C语言程序
sh脚本和C程序有一些相似之处,但它们在根本上是不同的。下面并列列出了一一个sh脚本和一个C程序,以比较它们的语法形式和用法。
sh | c | ||||||||
INTERPRETER: read&execute | COMPLIE-LINKED to a.out | ||||||||
mysh | a | b | c | d | a.out | a | b | c | d |
$0 | $1 | $2 | # | $4 | main{int argv,char *argv[]} |
首先,sh 是一个解释程序,逐行读取sh脚本文件并直接执行这些行。如果行是可执行命令且为内置命令,那么sh可直接执行。否则,它会复刻一个子进程来执行命令,并等待子进程终止后再继续,这与它执行单个命令行完全一样。相反,C程序必须先编译链接到一个二进制可执行文件,然后通过主sh的子进程运行二进制可执行文件。其次,在C程序中,每个变量必须有一个类型, 例如char、int、 float、 派生类型(如struct)等。相反,在sh脚本中,每个变量都是字符串
。因此不需要类型,因为只有-一种类型,即字符串。最后,每个C程序必须有一个main()函数,每个丽数必须定义一个返回值类型和参数(如有)。相反,sh脚本不需要main函数。在sh脚本中,第一个可执行语句是程序的入口点。
3.命令行参数
可使用与运行sh命令完全相同的参数调用sh脚本,如:
mysh one two three
在sh脚本中,可以通过位置参数$O、$1、$2等访问命令行参数。前10个命令行参数可以作为$0$9被访问。其他参数必须称为${10}${n},其中n>10。或者,可以通过稍后显示的shift命令查看它们。通常,$0是程序名本身,\(1 到\)u是程序的参数。在sh中,可用内置变量$#
和$*
计数并显示命令行参数。
$#=命令行参数\(1到\)n的数量
$*=所有命令行参数,包括\(0
此外,sh还有与命令执行相关的以下内置变量。
\)S=执行sh的进程PID
$?=最后-一个命令执行的退出状态(如果成功,则为0,否则为非0)
4.sh变量
sh有许多内置变量,如PATH、HOME、TERM等。除了内置变量外,用户还可使用任何符号作为sh变量。不需要声明。所有的sh变量值都是字符串。未赋值的sh变量是NULL字符串。sh变量可用以下方法设置或赋值:
echo A | A | |
echo $A | {null if variable A is not set} | |
A="this is fun" | #set A value | |
echo $A | this is fun |
如果A是一个变量,则$A是变量的值
5.sh中的引号
sh有许多特殊字符,如$、/、*、>、<等。要想把它们用作普通字符,可使用\或单引号来引用它们。
通常,\用于引用单个字符。单引号用于引用长字符串。单引号内没有替换。双引号用于保留双引号字符串中的空格,但在双引号内会发生替换。
6.sh语句
sh语句包括所有Unix/Linux命令,以及可能的I/O重定向。如:
ls
ls > outfile
date
cp f1 f2
mkdir newdir
cat < fil ename
此外,sh 编程语言还支持控制sh程序执行的测试条件、循环、case 等语句。
7.sh命令
7.1内置命令
sh有许多内置命令,这些命令由sh执行,不需要创建一-个新进程。 下面列出一些常用的内置sh命令。
●.file: 读取并执行文件。
●break [n]:从最近的第n个嵌套循环中退出。
●cd [dirname]:更换目录。
●continue [n]:重启最近的第n个嵌套循环。
●eval[arg ..计算一 次参数并让sh执行生成的命令。
●exec [ag..]: 通过这个sh执行命令,sh将会退出。
●exit[n]: 使sh退出,退出状态为n。
●export[var ]:将变量导出到随后执行的命令。
●read [var.. ]: 从stdin中读取-行并为变量赋值。
●set [arg..]:在执行环境中设置变量。
●shift: 将位置参数$2$3..重命名为$1 $2..
●trap [arg] [a]:接收到信号n后执行参数。
●umask [ddd]:将掩码设置为八进制数ddd的。
●wait [pid];等待进程pid,如果没有给出pid,则等待所有活动子进程。
read命令:当sh执行read命令时,它会等待来自stdin的输人行。它将输人行划分为几个标记、分配给列出的变量。read 的一一个常见用法是允许用户与正在执行的sh进行交互。
7.2Linux命令
sh可以执行所有的Linux命令。其中,有些命令几乎已经成为sh不可分割的一部分,因为它们广泛用于sh脚本中。下文列出并解释了其中一些命令。
echo命令:echo只是将参数字符串作为行回显到stdout。它通常将相邻的多个空格压缩为一个空格,除非有引号。
expr命令:首先,它将两个参数字符串转换为数字,然后对数字执行(二进制)操作OP,再将得到的数字转换回字符串.同样,expr 也可用于对其值为数字字符串的sh变量执行其他算术操作。
管道命令:在sh脚本中经常使用管道作为过滤器。
其他命令:
●awk:数据处理程序。
●cmp: 比较两个文件。
●comm:选择两个排序文件共有的行。
●grep:匹配一系列文件的模式。
●diff: 找出两个文件的差异。
●join:通过使用相同的键来连接记录以比较两个文件。
●sed:流或行编辑命令。
●sort:排序或合并文件。
●tail: 打印某个文件的最后n行。
●tr:一对一字符翻译。
●uniq:从文件中删除连续重复行。
8.命令替换
在sh中,$A会被替换成A值。同样,当sh遇到'cmd' (用引号括起来)或$(cmd)时,
它会先执行cmd,然后用执行的结果字符串替换$(cmd)。
9.sh控制语句
sh是一种编程语言,支持许多执行控制语句,类似于C语言中的语句。
sh支持以下语句:
if-else-fi语句
for语句
while语句
until-do语句
case语句
continue和break语句
10.I/O重定向
当进入sh命令时,我们可以指示sh将I/O重定向到除默认stdin、stdout 和sterr以外的文件。I/O重定向有以下形式和含义:
> file stdout转向文件,如果文件不存在,将会创建文件。
>> file stdout追加到文件。
< file 将文件用作stdin;文件必须存在并具有r权限。
<< word 从“here"文件中获取输人,直到只包含“word” 的行。
11.嵌入文档
可以指示输出命令从stdin获取输入,将其回显到stdout,直到遇到预先安排的关键字。
echo << END
# keep enter and echo lines until a line with only
END
cat << DONE
# keep enter and echo lines until
DONE
这些文档通常被称为嵌入文档。它们通常用在sh脚本中,以生成长块的描述性文本,不需要分别回显每一行。
12.sh函数
sh函数的定义为:
func()
{
# function code
}
由于sh逐行执行命令,所以必须在任何可执行语句之前定义sh脚本中的所有函数。与C语言不同,在sh脚本中无法声明函数原型。sh 函数的调用方式与sh脚本文件的执行方式完全相同。sh语句
func s1,s2,……,sn
调用sh函数,以参数(字符串)形式传递s1~sn。在被调函数中,参数被引用为\$0、\$1到\$n。通常,$0是函数名,\$1到\$n是与命令行参数对应的位置参数。函数执行结束时,\$?表示其退出状态,如果成功,状态为0,否则,状态为非0。\$?值可用函数的显式返回值进行更改。但是,为了测试\$?的最后一次执行,必须将其分配给一个变量,然后测试该变量。
13.sh中的通配符
星号通配符:sh中最有用的通配符是*,可扩展到当前目录中的所有文件。
?通配符:查询某文件名中的字符
[]通配符:查询文件名中-一对[]中的字符。
14.命令分组
在sh脚本中,可以用{}或)对命令进行分组。
{ls;mkdir abe;ls;} :通过当前sh执行{}中的命令列表。{} 命令分组的唯一用处是在 相
同环境下执行这些命令,例如,为分组中的所有命令重定向l/O。
更有用的命令分组是(),由subsh (进程)执行。
(cd newdir;ls;A=value;mkdir $A):通过subsh进程执行()中的命令。subsh 进程可在不
影响父sh的情况下更改其工作目录。此外,当subsh进程终止时,subsh 中的任何赋值变量
都不起作用。
15.eval命令
eval是sh的-一个内置命令。它由sh自己执行,而不需要复刻新进程。它将输入参数字符串连接到一个字符串中,计算一次,即执行变量和命令替换,然后给出结果字符串供sh执行。
sh在执行命令行之前,按以下步骤计算该命令行。
1.参数替换:扫描命令行,将任何\$x替换为它的值,但只执行一次,即不能再次替换任何产生的\$符号。
2.命令替换:用替换执行'pwd'。即先运行指令赋值后再运行命令
3.通配符扩展:当sh执行该行时,它将new*展开为以abc开头的文件名。这将会把所有以abe开头的文件名复制到目标目录中。
16.调试sh脚本
sh脚本可由带有-x选项的子sh运行,以进行调试,如:
bash -x mysh
子sh将在执行命令之前显示要执行的每个sh命令,包括变量和命令替换。它允许用户
跟踪命令执行。如果出现错误,sh将在错误行上停止并显示错误消息。
17.sh脚本的应用
sh脚本最常用于执行涉及冗长命令序列的常规作业,如下:
- 大多数用户都有将Linux安装到计算机上的经验。Linux 安装包是用sh脚本编写的。在安装过程中,它可以与用户交互,查找用户计算机上可用的硬盘,分区并格式化磁盘分区,从安装媒体下载或解压文件并将文件安装到它们的目录中,直到安装过程完成。试图手动安装Linux会非常烦琐,几乎不可能实现。
- 当用户登录Linux系统时,登录进程会执行一系列sh脚本,
18.编程:用sh脚本递归复制文件
“此用户很懒,没有填写以下内容,尝试催促一下他吧”
参考文献
Bourne, S.R., The Unix System, Addison Wesley, 1982
Forouzan, B.A., Gilberg, R.F,, Unix and Shell Programming, Brooks/Cole, 2003
Wang, K.C., Design and Implementation of the MTX Operating System, Springer International Publishing AG, 2015
总结
程序设计语言必备的要素和技能
要素
- 语法:语法是程序设计语言的基础,它规定了如何组织代码和表达意思,必须熟悉语法才能编写有效的代码。
- 数据类型:程序设计语言中的基本数据类型包括整数、浮点数、字符、布尔值等,必须了解这些数据类型的特点和用法。
- 变量和常量:变量是可以改变的值,常量是不可改变的值,必须掌握如何定义、声明和使用变量和常量。
- 流程控制:程序设计语言必须提供流程控制机制,如条件语句、循环语句、跳转语句等。
- 函数和模块:函数和模块是代码的重要组成部分,必须了解如何定义、调用和使用函数和模块。
技能
- 调试和测试:对程序进行调试和测试是编程的重要环节,必须了解调试和测试的方法和技巧。
- 善于使用网络:
不会就去网上抄。 - 持续学习:程序设计语言不断发展和更新,必须保持学习和掌握最新的语言特性和技巧。
这些要素和技能在shell脚本中是如何呈现出来的
- 变量:使用变量来存储和操作数据。在shell脚本中,变量可以通过赋值给特定的字符串或数字来创建。
- 流程控制:使用条件和循环语句来控制程序的流程。例如,使用if语句来执行特定的代码块,基于特定的条件判断。循环语句如for和while用于在指定条件下重复执行一系列语句。
- 输入和输出:使用命令行参数或标准输入来接收用户的输入,并使用标准输出来显示结果。可以使用read命令读取用户的输入,使用echo命令将内容输出到屏幕上。
- 函数:使用函数来封装一系列相关的命令或代码块,并在需要时进行调用。函数可以接受参数,并返回一个结果。使用function关键字定义函数,例如function greeting() { echo "Hello, $1!"; }。
- 正则表达式:使用正则表达式来匹配和处理字符串。可以使用grep和sed等工具来执行正则表达式操作。
- 文件操作:可以使用命令来创建、复制、移动和删除文件,以及读取和写入文件的内容。例如,cp命令用于复制文件,cat命令用于显示文件的内容。
- 管道和重定向:使用管道将命令的输出传递给另一个命令进行处理。使用重定向将命令的输入或输出重定向到文件或其他设备。例如,
command1 | command2
将command1的输出作为command2的输入。 - 环境变量:使用环境变量来存储和访问系统的信息。可以使用export关键字将变量导出为环境变量,以便在其他shell脚本中使用。
苏格拉底挑战
学习sh脚本知识点
当然可以,让我们开始学习sh脚本知识点,通过苏格拉底式的提问方式:
- 你知道sh脚本的全名是什么吗?
很好!你对Bourne shell脚本的了解有一定基础。接下来的问题:
- Bourne shell脚本通常用于执行哪些任务或操作?
非常正确!Bourne shell脚本是一个强大的工具,用于自动化和执行各种系统任务。接下来的问题:
- 在sh脚本中,如何注释代码以提高代码的可读性?
单行注释:在行的开头添加#号符号来注释代码,#号后面的内容将会被忽略。多行注释:使用一对奇数个反斜杠的符号`(例如三个)来注释多行代码,反斜杠符号之间的所有内容均被忽略。
非常好,你对注释的理解是正确的。注释对于代码的可读性和维护非常重要。接下来的问题:
- 在sh脚本中,如何声明和使用变量?
声明变量:
使用
符号来给变量赋值,等号两侧不能有空格。例如:
name="Tom"
age=18
使用变量:
在脚本中使用变量时,需要在变量名前加上
$
符号。例如:
echo "我的名字是 $name,今年 $age 岁。"
非常棒的解答!你对于变量的声明和使用已经很明了。接下来的问题:
- 在sh脚本中,如何进行条件判断以执行不同的代码分支?
在sh脚本中,可以使用if语句进行条件判断。if语句的语法如下:
if [ condition ]
then
#执行语句1
else
#执行语句2
fi
其中,
[ condition ]
表示条件判断语句,如果判断为真,则执行
then
语句块中的代码;否则执行
else
语句块中的代码。
then
和
else
语句块中的代码可以包含任意数量的语句,每个语句之间需要分号隔开。
非常好,你对条件判断结构的描述非常清晰。接下来的问题:
- 在sh脚本中,如何使用循环结构执行重复的任务?
for循环:
for循环用于按照指定的次数或列表来重复执行一段代码。while循环:
while循环用于在满足指定条件的情况下循环执行一段代码。while循环的语法如下:
你提到了两种常见的循环结构:for循环和while循环,这是很重要的。接下来的问题:
- 在sh脚本中,如何读取用户的输入并将其存储在变量中?
read
命令来读取用户的输入,并将其存储在变量中。
正确!使用read
命令可以接收用户的输入并将其保存到变量中,这在与用户交互的脚本中非常有用。接下来的问题:
- 请解释一下sh脚本中的函数是什么,以及如何定义和调用函数?
在sh脚本中,函数的定义和调用都比较简单。
函数定义
在sh脚本中,函数的定义格式如下:
function_name () {
#执行的命令和代码
}
其中,
function_name
是函数名,不同于变量名,函数名可以包含字母、数字、下划线等字符。
{}
内是执行的命令和代码。
如果需要将参数传递给函数,只需在函数定义中在函数名后加上括号,括号中写上接受的参数名,如下所示:
function_name () {
#执行的命令和代码
}
function_name_with_parameters () {
parameter1=$1
parameter2=$2
#执行的命令和代码
}
其中,
parameter1
和
parameter2
分别为第1个和第2个参数的变量名。
函数调用
在函数定义完成后,即可在脚本的任何地方调用函数。函数调用格式如下:
function_name
如有参数,可以使用以下方式传递参数:
function_name_with_parameters parameter1 parameter2
非常详细的解释!你对于函数的定义和调用有了清晰的理解。接下来的问题:
- 在sh脚本中,如何处理命令行参数并在脚本中使用它们?
$1
、
$2
等系统变量来访问命令行参数。 这些系统变量用于捕获传递给脚本的参数,并可以在脚本中进行处理和使用。
正确!使用$1
、$2
等系统变量可以访问命令行参数,这些变量分别表示第一个、第二个命令行参数,以此类推。这对于脚本处理命令行输入非常有用。接下来的问题:
- 在sh脚本中,如何将输出重定向到文件或从文件中读取输入?
>
、
>>
、
<
)来实现对文件的输出重定向和从文件中读取输入。
正确!重定向符号(>
、>>
、<
)用于控制脚本的输入和输出,非常有用。接下来的问题:
- 请解释一下sh脚本中的条件测试,以及如何使用条件测试来判断某个条件是否成立。
您的理解非常不错!您已经掌握了许多关于sh脚本的重要知识点,包括变量、条件语句、循环、函数、命令行参数和重定向等方面的基本概念。如果您有进一步的问题或需要更深入的了解,请随时提出。
你的回答非常出色,总结如下:
- 你清晰地解释了Bourne shell脚本的全名以及它通常用于执行的任务和操作。
- 你详细介绍了如何在sh脚本中添加注释以提高代码的可读性。
- 你解释了如何声明和使用变量,并提到了变量名不区分大小写。
- 你对条件判断结构的解释很清晰,包括if语句的使用。
- 你介绍了for循环和while循环,两者在sh脚本中用于执行重复的任务。
- 你清晰地解释了如何使用
read
命令来读取用户的输入并将其存储在变量中。 - 你详细描述了函数的概念,包括如何定义和调用函数,以及如何传递参数给函数。
- 你解释了如何处理命令行参数,使用
$1
、$2
等系统变量来访问它们。 - 你提到了重定向符号,用于控制输入和输出,这在脚本编程中非常重要。
总的来说,你对sh脚本的知识点有很好的理解,表达也很清晰。如果你继续深入学习和实践,将能够编写更复杂和功能丰富的脚本。如果你有任何进一步的问题或需要更多帮助,请随时提问!