第十章 Shell基础
1.Shell基础知识
1.1.Shell程序设计语言
Shell是所有Linux系统所共有的一个工具,它提供了用户和系统内核进行交互的环境。用户是无法直接操控内核和硬件的。通常,Shell是系统登录后始终运行的程序,它的任务就是随时接收用户输入的命令,并把这些命令翻译成内核能够是被的形式,传递给内核,以完成用户和内核之间的交互。
![image-20230922112009105](https://i-blog.csdnimg.cn/blog_migrate/c3f4074496a5e15c539e4dee0c595b16.png)
Shell是Linux系统的最外层。用户通过Shell实现和操作系统的交互。操作系统负责管理系统的全部软硬件资源,因此,用户对系统资源的操作命令最终都要通过Shell传递给操作系统,再由操作系统实现对系统资源的操作。从这个角度考虑,Shell也是用户与操作系统之间的一种通信方式。
这个通信方式有两种实现,即采用交互的方式或者非交互的方式解释和执行用户命令。交互方式是指用户直接从键盘输入命令,系统立刻响应,返回结果给用户。如果用户对系统资源的操作要通过多条命令的执行才能完成,则更好的方式是采用非交互的方式实现。非交互方式是指采用Shell脚本语言(Shell Script)进行脚本的开发。Shell脚本是放在文件中的一系列Shell命令和预先设定好的操作系统命令的组合,可以被重复使用。只要赋予Shell脚本的可执行权限,则运行此脚本时,脚本中的命令会一次性自动解释和执行。因此,Shell脚本语言作为程序设计语言,和其他的高级程序设计语言相似,也定义了各种变量和参数,并提供了包括循环结构和分支结构等在内的控制结构。
1.2.Shell的种类
Shell作为命令解释器,既可以再UNIX系统中使用,也可以再Linux系统中使用,主要用来解释各种Shell命令,以实现和操作系统的交互。造的UNIX系统中常用的3中Shell是bsh、csh、ksh,后来形成众多Shell版本都是在这3种Shell的扩展和结合。Ubuntu默认的Shell是bash,它由bsh发展而来。如果想查看Ubuntu种支持哪些Shell,可以输入以下命令查看:
cat /etc/shells
几种常用的Shell的主要特性:
- bsh:是最经典的Shell,通用于UNIX和Linux平台,适用于编程。
- csh:支持C语言,编程时,其语法和C语言相似。
- ksh:是bsh的高级扩展,执行命令高效驯熟。它融入了很多csh的特性,是bsh和csh的集合。
- bash:基于bsh开发的GNU自由软件,符合POSIX便准,具有很多优点。
echo $BASH_VERSION #输出当前系统中Shell的版本
1.3.Shell的便捷操作
- **对于文件名的自动补齐功能:**用户输入一部分文件名后,按下Tab键,系统会自动补齐后半部分。
- **对于命令的自动补齐功能:**用户输入某一命令的第一个字母后,按下Tab键,实现该命令的自动补齐。如果用户输入的不是这个命令,可以连续两次按下Tab键,让系统把所有以该命令开头的命令都列出来。
1.4.Shell中的特殊字符
Shell中定义了一些特殊字符,包括文件名通配符、输入输出重定向符、管道符、命令执行控制符、命令组合符、元字符、转义符等。Shell在读入命令行后,要先对这些特殊字符进行响应的处理,以确定要执行的程序和参数以及执行的方式。
1.4.1.文件名通配符
- **文件名通配符:**通常情况下,利用通配符对多个文件进行处理操作,以简化操作步骤。
*
用来匹配任何字符串,包括空串?
用来匹配单个字符[]
用来匹配方括号内给出的某个单个字符[字符1,字符2...]
用来匹配方括号内列出的多个字符,字符间用逗号分隔[开始字符-结束字符]
用来匹配方括号内用字符范围的形式给出的多个字符[!字符]
用来指定不应出现的字符
- 举例说明:
- file*:表示匹配以file开头的所有文件名
- fil?.exe:表示匹配fil开头,以.exe结尾,中间为任意一个字符的文件名
- *[!e]:表示匹配不以e结尾的文件名
- [a-c]*:表示匹配以a、b、c开头的所有文件名
- {a,b,c…}:表示一次性操作多个文件
ls -d /etc/pa* # 显示所有的pa开头的目录和文件
ls -d /etc/*conf* # 显示etc下所有含有conf的文件或目录
------------------
ls -ld /dev/sd? # 以长格式列出所有以sd开头的3个字符的文件信息
------------------
ls /dev/[df]?? # 列出dev目录下以d或者f开头并且文件名为3个字符的文件
ls /dev/[a-c]* # 列出dev目录下以a,b,c开头的所有文件
ls /dev/[!fhi]* # 列出dev目录下不是以f,h,i开头的所有文件
------------------
mkdir /tmp/{a,b,c} # 一次性创建/tmp/a,/tmp/b,/tmp/c三个目录
touch /tmp/{a,b,c}.txt # 一次性创建/tmp/a.txt,/tmp/b.txt/tmp/c.txt三个文件
1.4.2.输入输出重定向与管道符
-
**输入输出重定向符与管道符:**Linux系统中的绝大多数程序在运行时都要进行输入和输出的操作。输入操作告诉程序所要处理的数据,输出操作则将程序的结果显示出来。由于Linux系统一切接文件,因而Linux系统也用文件来描述系统的硬件资源,在用户通过操作系统处理信息的过程中,包括以下几类交互设备:
-
标准输入(Stdin):默认的设备是键盘。文件描述符为0,命令从标准文件中读取在执行过程中需要输入数据。
-
标准输出(Stdout):默认的设备是显示器。文件描述符为1,命令将执行后的输出结果发送到标准输出文件中。
-
标准错误(Stderr):默认的设备是显示器。文件描述符为2,命令将执行时的错误信息发送到标准错误文件中。
标准输入、标准输出和标准错误默认使用了键盘和显示器作为关联的设别,因此当执行命令时,会从用户接收用户的输入字符,并将命令显示在屏幕上,如果命令执行错误,也会将错误信息执行在屏幕上并反馈给用户。这样通过最普通的终端设备,用户就可以执行Linux命令,并完成最基本的输入输出操作。
-
![image-20211106182736724](https://i-blog.csdnimg.cn/blog_migrate/ec3790e3f3de3f33f03861c02da232c2.png)
1.4.2.1.标准输出重定向
相比输入重定向,输出重定向使用得更加频繁,我们一般所说得重定向都是属于输出重定向。
输出重定向用">“或”>>"操作符,分别用于覆盖或追加文件内容。
ls /home > 'home.txt' # 将home里得内容重定向到home.txt文件
ls # 当前创建了一个home.txt的文件
cat 'home.txt'
ls /etc > 'home.txt' # /etc的内容会覆盖/home的内容
ls /home >> 'home/txt' # 追加/home的内容进home.txt
cat 'home.txt'
所以在使用">"重定向时,一定注意,不要造成不必要的数据丢失。
灵活使用重定向,可以实现很多其他功能。比如执行下面的命令就可以将1.txt和2.txt这两个文件的内容合并到3.txt中:
echo 'Hello' > 1.txt # 将hello写入1.tx
echo 'World' > 2.txt # 将world写入2.txt
cat 1.txt 2.txt # 同时显示两个文件的内容
cat 1.txt 2.txt > 3.txt # 将1和2的内容重定向到3中
1.4.2.2.标准输入重定向
输入重定向就是将命令接收的途径由默认键盘重定向到指定文件,输入重定向需要使用"<"操作符。
cat < /etc/passwd # 通过输入重定向查看/etc/passwd的内容
我们可以发现有"<“符和直接执行cat命令是一样的,是因为使用cat命令进行输出时默认使用了”<"符号。
我们直接执行cat命令,就会把我们想输出的内容输出到屏幕上,它会一直读取cat所要接收的内容,直到执行了Ctrl+D才能退出。
除了"<“符之外,标准输入重定向还定义了”<<",它表示在此处创建文档:
cat << EOF # EOF=end of file
>aaa
>bbb
>ccc
>EOF
aaa
bbb
ccc # 会将内容原样输出
1.4.2.3.标准错误重定向
标准错误重定向就是将执行过程中出现的错误信息(如选项参数的错误)重新定向保存到指定文件中,而不是直接显示在频幕上。由于标准错误的描述符为2,因而标准错误重定向的标识符为“2>‘.其实在之前的标准输入输出重定向种省略了0和1的描述符。
ls home2 > home.txt # 因为没有home2的目录,所以会报错
ls home2 2> home.txt # 不会报错,而是把错误信息写进home.txt
cat home.txt # 查看错误信息
--------------------
# find / -user student这条命令既有正确信息,也有错误信息
find / -user student # 按文件是所有者查找,既有正确的,也有错误的
find / -user student > student.txt # 错误信息显示
find / -user student 2> student2.txt
cat student2.txt
--------------------
find / -user student 2> /dev/null # 将错误信息写到黑洞里
拓展:重定向中还有"&>"表示将所有的信息都重定向到指定文件。
1.4.2.4.管道符“|”
通过管道符可以把多个简单的命令连接起来,实现更加复杂的功能。
**管道符"|“用于连接左右两个命令,将管道”|“左边执行的结果作为管道”|“右边的输入。**这样”|"就像一根管道一样连接左右两条命令,并在管道中实现数据从左至右的传输。
如ls命令与more命令通过管道符组合便可以实现目录列表分页展示的功能。
ll -h /etc | more # 分页展示etc目录下所有文件及子目录的信息
-------------------
grep -v "^#" /etc/ssh/sshd_config | grep -v "^$" # 找不不是空白行也不是#开头的行
ls命令与grep命令使用管道符组合可以只显示目录列表中包含特定关键字的列表项。
ls -lh /etc | grep net # 显示etc目录下所有net关键字的目录和详细信息
-rw-r--r--. 1 root root 22 10月 23 2020 issue.net
-rw-r--r--. 1 root root 767 8月 9 2019 netconfig
-rw-r--r--. 1 root root 58 10月 13 2020 networks
drwxr-xr-x. 2 root root 6 4月 11 2018 xinetd.d
find /etc -name "*.conf" -type f | wc -l # 统计etc下所有以conf结尾的文件的个数
-----------------------
head -n /etc/passwd | tail -n 1 # 取出/etc/passwd的前10行中的最后一行
1.4.3.命令组合符和元字符
常用的命令组合符有双引号(“”)、单引号(‘’)、单撇反引号(`)。在字符串中含有空格时,应使用双引号括起来,作为整体解析字符串;单引号把字符串括起来,以阻止shell解析变量;单撇反引号用于指示把执行命令的结果存放在变量中。
系统中的元字符有#、$和空格。#用于注释,它后面的内容不被Shell执行;$是变量的引用符,要访问变量的值,需要在变量前加$;空格是分隔符,用于分隔命令名、参数、选项等。
![image-20230926160716585](https://i-blog.csdnimg.cn/blog_migrate/c558742369609abc6c2fd33379d10c8d.png)
1.4.4.转义符
转义符用反斜线(“\”)来表示,它的作用是消除后面的单个元字符的特殊含义,阻止Shell后\
后面的字符解释为特殊字符。
例如把abc变量赋值给var,$abc的值为空,var变量的值也是空的。为变量赋值时使用转义符\
,var变量的值为\$abc
,组织了Shell的解释,仅把$作为一个普通字符。
![image-20230926161251855](https://i-blog.csdnimg.cn/blog_migrate/ad0e05b88ac621984640b4fb5ec78067.png)
2.Shell变量
2.1.Shell变量的种类
通过变量的定义和使用,可以方便用户进行Shell脚本程序的开发。Shell变量的定义是编写高效的系统管理脚本的基础,是脚本程序开发不可或缺的组成部分。在Shell中,根据用途和定义方式的不同,变量可以分为3类:环境变量、内部变量、用户变量:
-
环境变量:系统预定义的一组变量,不必由用户定义,它用于Shell提供有关运行环境的信息。环境变量定义在系统的启动文件中,Shell启动后,这些变量就存在并可以使用。常用的环境变量包括PATH等。
-
**内部变量:**Shell自定义的一组变量,是系统提供的,用户只能使用它,但不能对其进行修改。内部变量用于记录当前Shell的运行状态等一些信息。例如进程号等。
-
**用户变量:**用户在编写Shell程序的过程中定义的,是为实现用户编程目的而自定义的变量,允许用户对其进行修改。
变量的使用范围也是变量的作用域,根据变量作用域的不同,Shell变量可分为两类:本地变量和导出变量:
-
**本地变量:**也称为局部变量。如果在某Shell中定义的变量的作用域是局部的,即仅限于此Shell中,而在子Shell中是不存在的,不能使用。用户便令、环境变量、内部变量都可以输入本地变量。
-
**导出变量:**如果想使本地变量的作用域扩大,在它的子进程中也可以使用该变量,则需要对该变量进行导出操作,使之称为导出变量。用户变量、环境变量都可以进行导出操作,称为导出变量。导出变量的格式如下:
export 变量名
当前Shell:
![image-20230926174143005](https://i-blog.csdnimg.cn/blog_migrate/cce2e2321e11195e2c47ee4f03ca13df.png)
其他Shell:
2.2.Shell变量的定义及使用
- 环境变量:
变量 | 含义 |
---|---|
HOME | 用于保存注册目录的全部路径 |
PATH | 规定了命令执行时所搜索的路径 |
UID | 当前用户的UID,其值为数字构成的字符串 |
PWD | 当前工作目录的绝对路径 |
PSI | 主提示符,root用户的提示符为#,普通用户的提示符为$ |
TERM | 用户终端的类型 |
![image-20230926174632550](https://i-blog.csdnimg.cn/blog_migrate/af1a884a934eb88d075463274efa43f6.png)
- 内部变量:
变量 | 含义 |
---|---|
$0 | 当前Shell程序的名称 |
$# | 传送给Shell程序的位置参数的数量 |
$* | 调用Shell程序时所传诵的全部参数组成的单字符串 |
$? | 前一个命令或函数的返回值 |
$$ | 本程序的PID |
$! | 上一个命令的PID |
![image-20230926174931422](https://i-blog.csdnimg.cn/blog_migrate/37a2f658f6b7852e509695532aa429c8.png)
- **用户变量:**格式如下:
变量名=字符串 # 定义变量
uset 变量名 # 撤销变量
![image-20230926175147851](https://i-blog.csdnimg.cn/blog_migrate/f3b3b393311f2995d814010b7087880b.png)
2.3.变量的数值运算
Shell中可以使用算数运算符。但在默认情况下,Shell定义的变量是字符串类型的。Shell变量能过够存储的数字也只能是整数类型(简称整型)的数字字符串,例如2012、27等,Shell本身也没有数字运算的能力。
从上图可以看出,执行的结果并没有显示number1和number2的和30,因为Shell默认变量的类型是字符型。要想实现数值运算,必须进行变量的字符型和整型之间的转换。
declare
命令:该命令将声明变量为整型,使用-i
选项进行声明,i代表integer,整型。
![image-20230928105802369](https://i-blog.csdnimg.cn/blog_migrate/fd6aaf64b75d4825dfc7e159fd6b1ec5.png)
declare
命令的格式如下:
declare [+/-] [a][s][r][i][x]
declare
命令的选项及作用如下:
选项 | 作用 |
---|---|
+/- | -可以来设定变量的属性,+用来取消变量所设定的属性 |
a | 数字 |
f | 函数 |
i | 整数 |
r | 只读 |
x | 通过环境输出变量 |
![image-20230928110640990](https://i-blog.csdnimg.cn/blog_migrate/10f5726d5a02699639ccebafd6a3c691.png)
expr
命令:如果不适用declare
命令进行整型变量的声明,则可以使用expr
命令进行表达式的算数运算。expr
命令将数字字符串解释为整数,然后进行运算,得出结果。其语法格式如下:
expr 数值1 运算符 数值2
expr
命令支持的运算符类型如表:
运算符 | 含义 | 输出结果 |
---|---|---|
+,-,*,/,% | 加、减、乘、除、取余 | 数值 |
&,| | 逻辑与,逻辑或 | &运算:两个数值都非0时输出第一个数值,否则输出0; |运算:第一个数值非0时输出第一个数,否则输出第二个数。 |
=,==,!= | 等于,恒等于,不等于 | 结果为真时输出1,否则输出0 |
>,<,>=,<= | 大于,小于,大于等于,小于等于 | 结果为真时输出1,否则输出0 |
expr
命令的退出状态:算数运算的退出状态为0;逻辑运算和比较运算结果为真(即非0)时退出状态为0,为假时退出状态为1,出错时退出状态为2.
注意:
- 运算符两侧应保留空格。即运算符与运算符之间要用空格隔开;
- 如果算数运算符是Shell的元字符,所以必须进行转移,使其失去特殊含义;
- 算数运算符的数值必须为整数。
![image-20230928111839499](https://i-blog.csdnimg.cn/blog_migrate/f8f5d3d391eeb7b962f50e78ebf90003.png)
3.命令别名和命令历史
3.1.命令别名
如果某个长命令的使用频率非常高,则可以使用Shell提供的命令别名功能,即给某个命令另起一个名字。可以给使用频率非常高而且名字很长的命令起一个简短易记的别名。
使用alis
命令为命令起别名,语法格式如下:
alias 别名="原命令"
# 注意:等号两边无空格
![image-20230928112257931](https://i-blog.csdnimg.cn/blog_migrate/0eb87a23925e6e78a692f0361aed1277.png)
也可以直接使用alias
命令查看已有的别名:
![image-20230928112348786](https://i-blog.csdnimg.cn/blog_migrate/45a4b1a300f4704e530dc328c60066fb.png)
命令别名是有一定的生命周期的,别名只能在本次登录期间的当前Shell中有效,退出系统或切换Shell就自动失效了,要想让自定义的别名永久有效,可以把别名定义在管理员的主目录(/etc/passwd文件第5列规定的目录)。
使用unalias
命令可以取消别名:
![image-20230928112726698](https://i-blog.csdnimg.cn/blog_migrate/790d2aacb95add0e6ec789c0b4a97fd0.png)
3.2.命令历史
使用history
命令可以查看历史命令,语法格式如下:
historu [-c][n]
-c
选项表示清除当前Shell中的全部历史命令,n
表示列出前n条命令,n为数字。
![image-20230928112931561](https://i-blog.csdnimg.cn/blog_migrate/914960f3639bdddb49a3a15bdf2000a2.png)
grui/note-pic/raw/master/img/202309281127766.png" alt=“image-20230928112726698” style=“zoom:50%;” />
3.2.命令历史
使用history
命令可以查看历史命令,语法格式如下:
historu [-c][n]
-c
选项表示清除当前Shell中的全部历史命令,n
表示列出前n条命令,n为数字。
![image-20230928112931561](https://i-blog.csdnimg.cn/blog_migrate/914960f3639bdddb49a3a15bdf2000a2.png)