3. Linux shell基础

Linux shell是用户与Linux内核之间的交互接口,本质是一个命令解释器,它接收用户输入的命令,解析并传递给内核执行,最终将执行结果反馈给用户。 简单来说,shell就是Linux终端中“沟通用户与系统内核”的桥梁,是Linux系统操作、管理的核心工具。

Linux系统中存在多种shell(如Bash、Sh、Csh、Zsh等),其中Bash(Bourne Again Shell)是绝大多数Linux发行版(如Ubuntu、CentOS、RedHat、Debian等)的默认shell, 兼容性强、功能完善,支持命令补全、历史记录、脚本编程等实用特性,本章所有内容均基于Bash shell展开,确保贴合实际使用场景。

本章将从基础到进阶,详细讲解Linux shell的核心知识,重点覆盖最常用的Bash shell简介、环境变量的配置与使用、简单命令组合技巧、shell脚本基础语法及实操示例, 最后编写一个完整可运行的shell脚本,帮助学习者快速掌握shell操作,从“手动输入单条命令”过渡到“编写脚本实现自动化操作”,提升Linux使用效率。

3.1. Bash简介

Bash是Bourne Shell(Sh,Linux早期默认shell)的增强版本,于1989年发布,继承了Sh的所有核心功能,同时新增了大量实用特性,解决了Sh的功能局限, 成为目前Linux系统中最主流、最常用的shell。无论是普通用户的日常终端操作,还是管理员的系统维护、自动化脚本编写,Bash都是首选工具。

3.1.1. Bash的核心特点

Bash的优势在于功能强大、操作便捷,以下是其最核心的5个特点,也是日常使用中最常用到的功能:

  • 命令历史记录:自动记录用户输入的所有命令,通过history命令可查看完整历史记录;按上下箭头键可快速调用历史命令,无需重复输入,大幅提升操作效率。

  • 命令补全功能:输入命令、目录或文件名的前几个字符,按Tab键可自动补全;若有多个匹配项,按两次Tab键可显示所有匹配内容,避免输入错误,减少操作成本。

  • 命令别名:可自定义命令别名,将常用的复杂命令简化为简短的别名。例如,将ls -l别名设置为ll,后续输入ll即可执行对应命令,简化操作流程。

  • 脚本编程支持:支持编写shell脚本,将一系列命令按逻辑组合起来,赋予脚本可执行权限后,可一次性执行所有命令,实现自动化操作。

  • 兼容性强:完全兼容Sh shell的所有命令,同时支持扩展语法,如流程控制、函数、变量等,可满足不同场景的操作需求,无论是简单的终端命令,还是复杂的自动化脚本,都能轻松应对。

3.1.2. 查看当前shell及版本

在Linux终端中,可通过简单命令快速查看当前使用的shell以及Bash的版本,验证系统默认shell是否为Bash,操作如下:

  • 查看当前使用的shell:执行命令 echo $SHELL ,若输出结果为/bin/bash,说明当前使用的是Bash shell;若输出其他路径(如/bin/sh),则说明当前shell不是Bash。

  • 查看Bash版本:执行命令 bash --version ,执行后会显示Bash的具体版本号、编译时间及相关配置信息。不同Linux发行版的Bash版本可能略有差异,但核心功能完全一致,不影响日常使用。

3.1.3. Bash的启动方式

Bash的启动方式分为两种,对应不同的使用场景,日常操作中可根据需求选择:

  • 交互式启动:打开Linux终端,系统会自动启动Bash,进入交互模式。此时用户可手动输入命令,实时查看执行结果,适合日常手动操作。

  • 非交互式启动:通过执行shell脚本启动Bash,无需用户手动输入命令,脚本中的所有命令会按顺序自动执行,执行完成后自动退出,适合自动化任务。

3.2. 环境变量

环境变量是Linux系统中用于存储系统配置、用户偏好、路径信息等数据的“全局变量”,它贯穿于整个shell会话,影响shell的运行行为和命令的执行结果。 简单来说,环境变量就像是“系统的全局设置清单”,告诉shell和应用程序“去哪里找命令、去哪里找配置文件、当前用户是谁”等关键信息。

环境变量分为两类,适用范围不同,配置方式也有所区别,具体如下:

  • 系统环境变量:对系统中所有用户生效,无论哪个用户登录系统,都能使用这些环境变量,通常用于配置系统级别的参数,如系统命令路径、共享库路径等。

  • 用户环境变量:仅对当前用户生效,其他用户无法使用,通常用于配置用户个人的偏好,如自定义命令别名、个人命令路径等。

3.2.1. 常用环境变量介绍

Linux系统默认自带许多常用环境变量,掌握这些变量的含义,可更好地理解系统运行机制,解决日常操作中遇到的问题,以下是最核心、最常用的环境变量:

  • PATH:最核心的环境变量,用于指定shell查找可执行命令的路径。当用户输入一个命令(如ls、cat),shell会自动在PATH变量指定的路径中,按顺序查找对应的可执行文件,找到后立即执行;若未找到,会提示“command not found”(命令未找到)。

  • HOME:当前用户的家目录路径。例如,普通用户ubuntu的HOME变量值为/home/ubuntu,管理员用户(root)的HOME变量值为/root;输入cd ~命令,可快速切换到当前用户的家目录。

  • USER:当前登录用户的用户名。例如,登录用户为ubuntu,执行echo $USER命令,会输出ubuntu,可用于脚本中判断当前登录用户。

  • SHELL:当前使用的shell路径,默认值为/bin/bash,与echo $SHELL命令的输出一致。

  • PWD:当前所在目录的路径,与pwd命令的输出一致,可用于脚本中获取当前操作目录。

  • LD_LIBRARY_PATH:指定系统查找共享库文件(.so文件)的路径,类似于PATH变量,用于解决“共享库文件找不到”的问题。

  • LOGNAME:与USER功能一致,用于存储当前登录用户的用户名,兼容性更强,适合在脚本中使用。

3.2.2. 查看环境变量

在终端中,可通过两种方式查看环境变量,根据需求选择合适的方式,操作简单且直观:

  • 查看单个环境变量:

1
echo $变量名命令

输出示例如下:

1
2
3
4
5
6
7
8
9
#查找shell可执行命令的路径
echo $PATH
#信息输出如下
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/sbin:/sbin

#查看当前所在目录的路径
echo $PWD
#信息输出如下
/home/cat
  • 查看所有环境变量:

方式1:执行env命令,可查看系统中所有环境变量及其对应的值,输出结果按字母顺序排列,便于全面查看;

方式2:执行export命令,可查看当前用户的所有环境变量,输出结果更简洁,适合查看个人配置的变量。

输出示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#查看系统中所有环境变量及其对应的值
env
#输出信息如下
SHELL=/bin/bash
GST_GL_PLATFORM=egl
COGL_DRIVER=gles2
PWD=/home/cat
LOGNAME=cat
...

#查看当前用户的所有环境变量
export
#信息输出如下
declare -x COGL_DRIVER="gles2"
declare -x DISPLAY=":0"
declare -x GST_GL_API="gles2"
declare -x GST_GL_PLATFORM="egl"
declare -x HOME="/home/cat"
...

3.2.3. 临时设置环境变量

临时设置环境变量仅在当前shell会话中生效,关闭终端、重启系统,或切换到其他shell会话后,设置会自动失效,适合临时测试、临时修改配置。

临时设置环境变量的语法:

1
export 变量名=变量值

实操示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#临时添加一个自定义环境变量MY_NAME
export MY_NAME=Linux_Shell

#通过echo $MY_NAME可查看变量值
#关闭终端后,再次执行echo $MY_NAME,会无输出,设置失效
echo $MY_NAME
#信息输出如下
Linux_Shell


#临时修改PATH变量
#假设自定义命令存放在/home/cat/mycmd目录下
#其中$PATH表示保留原有路径,避免覆盖系统默认命令路径,添加后,shell会在该目录下查找命令
export PATH=$PATH:/home/cat/mycmd

#echo $PATH可查看变量值
#关闭终端后,再次执行echo $PATH,会无输出,设置失效
echo $PATH
#信息输出如下
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/sbin:/sbin:/home/cat/mycmd

3.2.4. 永久设置环境变量

永久设置环境变量会一直生效,即使关闭终端、重启系统,设置也不会丢失,适合长期使用的配置。

根据生效范围,分为两种配置方式,可根据需求选择:

  • 仅对当前用户生效

此种方法推荐普通用户使用,编辑当前用户家目录下的.bashrc文件(隐藏文件,可通过 ls -a ~ 命令查看),在文件末尾添加环境变量配置,步骤如下:

  1. 打开.bashrc文件:执行 vim ~/.bashrc

  2. 在文件末尾添加配置:

1
2
3
export MY_NAME=Linux_Shell

export PATH=$PATH:/home/cat/mycmd
  1. 保存并退出:按Esc键,输入:wq,回车保存并退出;

  2. 使配置生效:执行source ~/.bashrc,无需重启终端,配置立即生效;后续登录该用户,配置会自动加载。

  • 对所有用户生效

编辑系统全局配置文件/etc/profile,在文件末尾添加环境变量配置,步骤如下:

  1. 打开/etc/profile文件:执行 sudo vim /etc/profile

  2. 在文件末尾添加配置:

1
2
export MY_NAME=Linux_Shell
export PATH=$PATH:/opt/mycmd
  1. 保存并退出:按Esc键,输入:wq,回车保存并退出;

  2. 使配置生效:执行source /etc/profile,或重启终端、重启系统,所有用户登录后,该配置都会生效。

3.2.5. 删除环境变量

删除环境变量的方法:

  • 临时删除执行unset 变量名,如 unset MY_NAME

  • 永久删除需删除配置文件(.bashrc或/etc/profile)中对应的配置语句,再执行source命令生效。

3.2.6. 注意事项

  • 修改PATH变量时,务必使用$PATH保留原有路径,避免直接覆盖(如 export PATH=/home/cat/mycmd ),否则会导致系统默认命令(如ls、cat)无法找到,造成系统操作异常。

  • 普通用户无权限修改/etc/profile文件,必须使用sudo获取管理员权限;修改全局配置时,需谨慎操作,避免错误配置导致所有用户无法正常使用系统。

  • 若配置后不生效,可检查配置语句是否正确,或执行source命令重新加载配置文件,必要时重启终端。

3.3. 常用命令组合

在Linux终端中,单一命令的功能有限,通过“命令组合”可将多个命令串联起来,实现更复杂的操作,大幅提升工作效率。

常用的命令组合方式有三种:管道符(|)、重定向(>、>>、<)、逻辑运算符(&&、||),每种方式都有明确的使用场景,以下详细介绍其用法和实操示例。

3.3.1. 管道符

管道符(|)的核心作用是将前一个命令的输出结果,作为后一个命令的输入,实现命令的串联执行。

语法格式:

1
命令1 | 命令2

命令1执行后,不会将结果显示在终端,而是直接传递给命令2,由命令2对结果进行处理,最终输出处理后的结果;可多个管道符串联(命令1 | 命令2 | 命令3),实现多步处理。

实操示例1:查看系统中所有进程,并筛选出包含“ssh”的进程:

1
2
3
4
5
6
#命令
ps aux | grep ssh

#信息输出如下
cat       1206  0.0  0.0   5344   456 ?        Ss   13:35   0:00 /usr/bin/ssh-agent x-session-manager
root     13568  0.0  0.0   4572   648 ttyFIQ0  S+   13:40   0:00 grep ssh

ps aux命令用于查看系统中所有进程的详细信息,其输出结果通过管道符传递给grep ssh,grep命令用于筛选包含“ssh”关键字的内容,最终显示与ssh相关的进程。

实操示例2:多管道串联,查看系统内存使用情况,并筛选出“Mem”相关的内容:

1
2
3
4
5
#命令
free -h | grep Mem | awk '{print $2,$3,$4}'

#信息输出如下
1.9Gi 335Mi 1.3Gi

free -h查看内存使用情况,-h表示人性化显示,单位为GB/MB,管道符传递给grep Mem筛选内存相关内容,再传递给awk命令,提取第2、3、4列,对应总内存、已用内存、空闲内存。

3.3.2. 重定向

重定向(>、>>、<)的作用是改变命令的输入或输出方向,默认情况下,命令的输入来自键盘,输出显示在终端;通过重定向,可将命令的输出保存到文件, 或从文件读取输入,适合需要保存命令结果、批量处理文件的场景。

常用重定向符号及用法:

  • 覆盖重定向(>)

语法格式:

1
命令 > 文件名

将命令的输出写入指定文件,若文件已存在,会覆盖文件原有内容,需慎用,避免误删重要数据。

实操示例:

1
ls -l /home > home_files.txt

将/home目录下的文件详细信息,写入home_files.txt文件;若该文件已存在,原有内容会被彻底覆盖;若文件不存在,会自动创建该文件。

  • 追加重定向(>>)

语法格式:

1
命令 >> 文件名

将命令的输出写入指定文件,若文件已存在,会在文件末尾追加内容,不会覆盖原有内容。

实操示例:

1
echo "Hello Linux Shell" >> test.txt

将“Hello Linux Shell”这句话,追加到test.txt文件末尾;若文件不存在,会自动创建;执行多次该命令,内容会不断追加,不会覆盖之前的内容。

  • 输入重定向(<)

语法格式:

1
命令 < 文件名

将指定文件的内容,作为命令的输入,替代键盘输入,适合批量输入的场景。

实操示例:

1
2
3
4
5
#将test.txt文件的内容作为cat命令的输入,等同于cat test.txt,输出文件内容
cat < test.txt

#统计test.txt文件的行数,无需手动输入内容,直接读取文件进行统计
wc -l < test.txt

3.3.3. 逻辑运算符

逻辑运算符(&&、||)用于串联多个命令,根据前一个命令的执行结果,决定是否执行后一个命令, 适合需要条件判断的场景,无需编写复杂脚本,即可实现简单的逻辑控制。

常用逻辑运算符及用法:

  • 逻辑与(&&)

语法格式:

1
命令1 && 命令2

前一个命令执行成功(返回值为0),才会执行后一个命令;若前一个命令执行失败,后一个命令不执行。

实操示例:

1
mkdir test && cd test

先执行mkdir test创建test目录;若创建成功,则执行cd test进入test目录;若test目录已存在导致创建失败,则不执行cd命令,避免报错。

  • 逻辑或(||)

语法格式:

1
命令1 || 命令2

前一个命令执行失败,才会执行后一个命令;若前一个命令执行成功,后一个命令不执行。

实操示例:

1
cd test || mkdir test

先尝试执行cd test进入test目录;若进入失败,如test目录不存在,则执行mkdir test创建test目录;若进入成功,则不执行mkdir命令,避免重复创建。

  • 组合使用(&& + ||)

可将两个逻辑运算符组合使用,实现更复杂的条件判断。

语法格式:

1
命令1 && 命令2 || 命令3

实操示例:

1
2
3
4
5
6
#命令
mkdir test && cd test || echo "创建目录失败,请检查权限或目录是否存在"

#当test目录存在信息输出如下
mkdir: cannot create directory ‘test’: File exists
创建目录失败,请检查权限或目录是否存在

先创建test目录,创建成功则进入目录;若创建失败,如权限不足,则输出提示信息,告知用户失败原因,提升操作体验。

3.4. shell脚本语法

shell脚本是将一系列shell命令按逻辑顺序编写在一个文本文件中,赋予文件可执行权限后,可一次性执行所有命令,实现自动化操作。 shell脚本的后缀通常为.sh,语法简洁,无需编译,直接由Bash解析执行,适合批量处理、定时任务、系统监控等场景。

3.4.1. 脚本开头

1
#!/bin/bash

所有shell脚本的第一行,必须是#!/bin/bash(称为“shebang”或“释伴”),其核心作用是指定脚本的解释器为Bash,告诉系统用Bash shell来执行这个脚本。

需注意:

  1. #!/bin/bash这一行必须放在脚本的第一行,前面不能有任何字符,包括空格、注释,否则会失效;

  2. 若省略这一行,系统会默认使用当前shell解释器,可能不是使用Bash,而是Sh,有可能因语法不兼容导致脚本执行异常;

3.4.2. 注释

注释用于说明脚本的功能、代码的作用、作者、创建时间等信息,提高脚本的可读性和可维护性,注释内容不会被Bash解析执行。

shell脚本支持两种注释方式:

  • 单行注释

语法格式:

1
# 注释内容

用#开头,后面跟注释内容,仅对当前行有效。

示例:

1
2
3
# 这是单行注释,用于说明脚本的功能
# 脚本名称:test.sh
# 功能:输出当前用户和当前目录
  • 多行注释

语法格式:

1
:<<EOF 注释内容 EOF

其中EOF可替换为任意字符(如ABC、XXX),但前后字符必须一致,中间的内容均为注释。

示例:

1
2
3
4
5
6
7
:<<EOF
这是多行注释,用于说明脚本的详细信息
脚本名称:test.sh
作者:XXX
创建时间:2026年3月
功能:1. 输出当前登录用户;2. 输出当前所在目录;3. 查看系统内存使用情况
EOF

3.4.3. 变量

shell脚本中的变量用于存储数据,如字符串、数字、路径等,便于后续调用和修改,语法简洁,无需声明变量类型, 核心分为“系统变量”和“自定义变量”,前文已介绍系统变量,此处重点讲解自定义变量。

3.4.3.1. 自定义变量的定义

语法格式:

1
变量名=变量值

需注意:

  1. 等号两侧不能有空格,如name=Linux正确,name = Linux错误;

  2. 变量名由字母、数字、下划线组成,不能以数字开头,如user123正确,123user错误;

  3. 变量值若包含空格,需用双引号( "" )或单引号( '' )包裹,如 message="Hello Linux"

示例:

1
name="Linux Shell"、age=18、path=/home/cat

3.4.3.2. 变量的调用

语法格式:

1
$变量名 或 ${变量名}

推荐使用${变量名},避免变量名与其他字符混淆。

实操示例,创建test.sh文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash

# 定义自定义变量
name="Linux Shell"
age=18

# 调用变量
echo "变量name的值:$name"
echo "变量age的值:${age}"

# 拼接字符串
echo "欢迎学习${name},版本号:${age}.0"

添加执行权限并运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#添加执行权限
chmod a+x test.sh

#运行shell脚本
./test.sh

#信息输出如下
变量name的值:Linux Shell
变量age的值:18
欢迎学习Linux Shell,版本号:18.0

3.4.3.3. 变量的赋值

变量定义后,可重新赋值,覆盖原有值,

语法格式:

1
变量名=新变量值

实操示例,创建test.sh文件,内容如下:

1
2
3
4
5
6
7
8
#!/bin/bash

name="Linux"
echo "初始值:$name"

# 重新赋值
name="Linux Shell"
echo "重新赋值后:$name"

添加权限并执行脚本后,输出结果:

1
2
3
4
5
6
7
8
9
#添加执行权限
chmod a+x test.sh

#执行脚本
./test.sh

#信息输出如下
初始值:Linux
重新赋值后:Linux Shell

3.4.3.4. 特殊变量

shell脚本中有一些特殊变量,用于获取脚本参数、执行状态等,无需定义,直接调用,常用的有以下几个:

  • $0:获取当前脚本的文件名,包括路径;

  • $1-$9:获取脚本的命令行参数,$1是第一个参数,$2是第二个参数,以此类推;

  • $#:获取脚本的命令行参数个数;

  • $?:获取上一个命令的执行状态的返回值,0表示执行成功,非0表示执行失败;

  • $$:获取当前脚本的进程ID。

实操示例,创建test.sh文件,内容如下:

1
2
3
4
5
6
7
8
9
#!/bin/bash

# 特殊变量示例
echo "当前脚本文件名:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "参数总个数:$#"

echo "上一个命令执行状态:$?"

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#添加执行权限
chmod a+x test.sh

#执行脚本,添加输入参数
./test.sh 1 2

#信息输出如下
当前脚本文件名:./test.sh
第一个参数:1
第二个参数:2
参数总个数:2
上一个命令执行状态:0

3.4.4. 条件表达式

Shell中使用条件表达式主要通过test命令、[ ](中括号)或 [[ ]](双中括号,Bash特有),条件表达式主要分为3类:

  • 字符串比较表达式(字符串是否相等、为空、长度等)

  • 数值比较表达式(整数大小对比)

  • 文件测试表达式(判断文件类型/权限)

3.4.4.1. 字符串比较表达式

用法

表达式([[ ]] 写法)

等价写法([ ] 写法)

示例

判断字符串相等

[[ $str1 == $str2 ]]

[ “$str1” = “$str2” ]

[[ “abc” == “abc” ]]

判断字符串不相等

[[ $str1 != $str2 ]]

[ “$str1” != “$str2” ]

[[ “abc” != “def” ]]

判断字符串为空

[[ -z $str ]]

[ -z “$str” ]

[[ -z $str ]]

判断字符串非空

[[ -n $str ]]

[ -n “$str” ]

[[ -n $str ]]

判断字符串按ASCII码排序,str1大于str2

[[ $str1 > $str2 ]]

[ “$str1” > “$str2” ]

[[ “b” > “a” ]]

判断字符串按ASCII码排序,str1小于str2

[[ $str1 < $str2 ]]

[ “$str1” < “$str2” ]

[[ “a” < “b” ]]

判断字符串包含子串

[[ $str == sub ]]

无(不支持通配符)

[[ “hello” == ell ]]

判断字符串匹配通配符

[[ $str == abc? ]]

无(不支持通配符)

[[ “abc1” == abc? ]]

3.4.4.2. 数值比较表达式

用法

表达式([[ ]] 写法)

等价写法([ ] 写法)

示例

等于

[[ $num1 -eq $num2 ]]

[ $num1 -eq $num2 ]

[[ 5 -eq 5 ]]

不等于

[[ $num1 -ne $num2 ]]

[ $num1 -ne $num2 ]

[[ 5 -ne 3 ]]

大于

[[ $num1 -gt $num2 ]]

[ $num1 -gt $num2 ]

[[ 5 -gt 3 ]]

小于

[[ $num1 -lt $num2 ]]

[ $num1 -lt $num2 ]

[[ 3 -lt 5 ]]

大于等于

[[ $num1 -ge $num2 ]]

[ $num1 -ge $num2 ]

[[ 5 -ge 5 ]]

小于等于

[[ $num1 -le $num2 ]]

[ $num1 -le $num2 ]

[[ 3 -le 5 ]]

3.4.4.3. 文件测试表达式

用法

表达式([[ ]] 写法)

等价写法([ ] 写法)

示例

检查路径是否存在

[[ -e $path ]]

[ -e “$path” ]

[[ -e “”/etc/hostname”” ]]

检查是否为普通文件

[[ -f $file ]]

[ -f “$file” ]

[[ -f “”/etc/hostname”” ]]

检查是否为目录

[[ -d $dir ]]

[ -d “$dir” ]

[[ -d “/home” ]]

检查是否为符号链接

[[ -L $link ]]

[ -L “$link” ]

[[ -L “/etc/hosts” ]]

检查文件是否可读

[[ -r $file ]]

[ -r “$file” ]

[[ -r “/etc/hostname” ]]

检查文件是否可写

[[ -w $file ]]

[ -w “$file” ]

[[ -w “/etc/hostname” ]]

检查文件是否可执行

[[ -x $file ]]

[ -x “$file” ]

[[ -x “run.sh” ]]

检查文件是否非空

[[ -s $file ]]

[ -s “$file” ]

[[ -s “data.txt” ]]

检查是否为字符设备文件

[[ -c $device ]]

[ -c “$device” ]

[[ -c “/dev/tty” ]]

检查是否为块设备文件

[[ -b $device ]]

[ -b “$device” ]

[[ -b “/dev/sda1” ]]

检查文件1修改时间比文件新

[[ $file1 -nt $file2 ]]

[ “$file1” -nt “$file2” ]

[[ “file1.txt” -nt “file2.txt” ]]

检查文件1修改时间比文件旧

[[ $file1 -ot $file2 ]]

[ “$file1” -ot “$file2” ]

[[ “file1.txt” -ot “file2.txt” ]]

检查两个路径是否指向同一文件

[[ $path1 -ef $path2 ]]

[ “$path1” -ef “$path2” ]

[[ “/tmp/link” -ef “/tmp/file” ]]

3.4.5. 流程控制

流程控制用于控制脚本中命令的执行顺序,实现条件判断、循环执行等功能,是编写复杂脚本的核心, 以下重点讲解最常用的3种流程控制语句:if-else、for、while。

3.4.5.1. if-else 条件判断

用于根据条件判断,执行不同的命令,语法分为3种:单分支(if)、双分支(if-else)、多分支(if-elif-else),核心是条件表达式,判断是否成立。

  • 单分支(满足条件执行,不满足不执行):

语法格式:

1
2
3
4
if [ 条件表达式 ]; then
    命令1
    命令2
fi
  • 双分支(满足条件执行命令1,不满足执行命令2):

语法格式:

1
2
3
4
5
if [ 条件表达式 ]; then
    命令1
else
    命令2
fi
  • 多分支(多个条件判断,满足哪个执行哪个):

语法格式:

1
2
3
4
5
6
7
if [ 条件表达式1 ]; then
    命令1
elif [ 条件表达式2 ]; then
    命令2
else
    命令3
fi

实操示例,创建test.sh文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash

# 提示用户输入一个数字
echo "请输入一个数字:"

# read命令用于读取用户输入,赋值给num变量
read num

# 条件判断
if [ $num -gt 100 ]; then
    echo "你输入的数字大于100"
else
    echo "你输入的数字小于或等于100"
fi

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#添加执行权限
chmod a+x test.sh

#执行脚本
./test.sh

#信息输出如下
请输入一个数字:
10
你输入的数字小于或等于100

3.4.5.2. for 循环

用于循环执行一系列命令,适合已知循环次数的场景,如遍历列表、遍历文件等,语法有两种常用格式:

  • 遍历列表

语法格式:

1
2
3
4
for 变量名 in 列表; do
    命令1
    命令2
done

实操示例,创建test.sh文件,内容如下:

1
2
3
4
5
6
#!/bin/bash

# for循环遍历列表
for num in 1 2 3 4 5; do
    echo "当前数字:$num"
done

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#添加执行权限
chmod a+x test.sh

#执行脚本
./test.sh

#信息输出如下
当前数字:1
当前数字:2
当前数字:3
当前数字:4
当前数字:5
  • 数值范围循环

语法格式:

1
2
3
4
for ((变量名=起始值; 变量名<=结束值; 变量名++)); do
    命令1
    命令2
done

实操示例,创建test.sh文件,内容如下:

1
2
3
4
5
6
7
8
#!/bin/bash

# for数值范围循环
for ((i=1; i<=10; i++)); do
    # 计算平方,$(( ))用于执行算术运算
    square=$((i*i))
    echo "$i 的平方是:$square"
done

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#添加执行权限
chmod a+x test.sh

#执行脚本
./test.sh

#信息输出如下
1 的平方是:1
2 的平方是:4
3 的平方是:9
4 的平方是:16
5 的平方是:25
6 的平方是:36
7 的平方是:49
8 的平方是:64
9 的平方是:81
10 的平方是:100

3.4.5.3. while 循环

用于循环执行一系列命令,适合未知循环次数的场景,如持续获取用户输入、持续监控服务状态等,核心是循环条件,条件成立则继续循环,不成立则退出循环。

语法格式:

1
2
3
4
while [ 条件表达式 ]; do
    命令1
    命令2
done

实操示例,创建test.sh文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash

echo "请输入内容(输入exit退出):"
read input

# while循环,判断input是否为exit
while [ "$input" != "exit" ]; do
    echo "你输入的内容是:$input"
    echo "请继续输入(输入exit退出):"
    read input
done

echo "已退出循环,脚本结束"

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#添加执行权限
chmod a+x test.sh

#执行脚本
./test.sh

#信息输出如下
请输入内容(输入exit退出):
10
你输入的内容是:10
请继续输入(输入exit退出):
exit
已退出循环,脚本结束

3.4.6. 函数

函数用于将一系列命令封装起来,起一个名字,后续可通过函数名直接调用,避免重复编写相同代码,提升脚本的可读性和可维护性,适合脚本中多次使用的代码块。

语法格式:

1
2
3
4
5
6
7
函数名() {
    命令1
    命令2
    # 可选:return 返回值(0-255)
}

调用函数:函数名(直接输入函数名,无需加括号)

实操示例,创建test.sh文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash

# 封装函数:输出系统基础信息
system_info() {
    echo "====================系统基础信息=================="
    echo "当前登录用户:$USER"
    echo "当前所在目录:$PWD"
    echo "系统内核版本:$(uname -r)"  # $(命令)用于获取命令的输出结果
    echo "系统内存使用:$(free -h | grep Mem | awk '{print $3 "/" $2}')"
    echo "=================================================="
}

# 调用函数
system_info

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#添加执行权限
chmod a+x test.sh

#执行脚本
./test.sh

#信息输出如下
====================系统基础信息==================
当前登录用户:cat
当前所在目录:/home/cat
系统内核版本:4.19.232
系统内存使用:341Mi/1.9Gi
==================================================

3.4.7. 脚本的执行方式

编写完shell脚本后,需赋予脚本可执行权限,才能执行,常用的执行方式有3种,具体如下:

  • 方式1:赋予可执行权限后,直接执行

1
2
3
4
5
6
7
8
#赋予权限
chmod +x 脚本名.sh

#执行脚本,./表示当前目录,必须加,否则系统会在PATH路径中查找
./脚本名.sh

#或者使用绝对路径
/home/cat/脚本名.sh
  • 方式2:使用bash命令执行,无需赋予可执行权限

1
2
3
bash 脚本名.sh
或
sh 脚本名.sh
  • 方式3:使用source命令执行,无需赋予可执行权限,脚本中的变量会生效

1
2
3
source 脚本名.sh
或
. 脚本名.sh

执行后,脚本中定义的变量、别名等,会在当前shell会话中生效,适合配置类脚本。

3.5. 完整shell脚本示例

结合本章所学的所有知识,编写一个完整的shell脚本,实现系统状态监控功能,脚本包含注释、变量、函数、流程控制、命令组合等核心语法, 可直接运行,实用性强。

脚本功能:

  1. 输出脚本执行时间;

  2. 输出系统基础信息,包括登录用户、主机名、内核版本;

  3. 监控系统核心资源,包括CPU使用率、内存使用率、磁盘使用率;

  4. 判断资源使用率是否超过阈值,如果CPU>80%、内存>80%、磁盘>90%,超过则提示警告;

  5. 将监控结果保存到日志文件中;

  6. 支持命令行参数,可指定日志保存路径。

实操示例,创建system_monitor.sh文件,内容如下:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/bin/bash
# 脚本名称:system_monitor.sh
# 脚本功能:系统状态监控(CPU、内存、磁盘),并将结果保存到日志
# 作者:XXX
# 创建时间:2026年3月
# 用法:1. 赋予权限:chmod +x system_monitor.sh
#       2. 执行脚本:./system_monitor.sh (默认日志保存到当前目录的system_monitor.log)
#       3. 自定义日志路径:./system_monitor.sh /home/cat/system_monitor.log

# ====================== 定义变量 ======================
# 定义资源使用率阈值
CPU_THRESHOLD=80    # CPU使用率阈值(%)
MEM_THRESHOLD=80    # 内存使用率阈值(%)
DISK_THRESHOLD=90   # 磁盘使用率阈值(%)

# 定义日志路径
# $1 是第一个命令行参数,若存在则使用,否则使用默认路径
if [ $# -eq 1 ]; then
    LOG_PATH=$1
else
    LOG_PATH="./system_monitor.log"
fi

# ====================== 定义函数 ======================
# 函数1:输出脚本执行时间
get_current_time() {
    # date +"%Y-%m-%d %H:%M:%S" 用于获取当前时间,格式:年-月-日 时:分:秒
    echo "==================== 执行时间:$(date +"%Y-%m-%d %H:%M:%S") ===================="
}

# 函数2:输出系统基础信息
get_system_info() {
    echo "--------------------- 系统基础信息 ---------------------"
    echo "当前登录用户:$USER"
    echo "系统主机名:$(hostname)"    # hostname命令获取主机名
    echo "系统内核版本:$(uname -r)"  # uname -r获取内核版本
    echo "--------------------------------------------------------"
}

# 函数3:监控CPU使用率
monitor_cpu() {
    echo "--------------------- CPU使用率监控 ---------------------"
    # top -bn1 :非交互式执行top命令,获取一次CPU信息;grep "Cpu(s)" 筛选CPU相关行;awk提取使用率(第2列)
    CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d. -f1)
    echo "当前CPU使用率:${CPU_USAGE}%"

    # 条件判断:若CPU使用率超过阈值,输出警告
    if [ $CPU_USAGE -gt $CPU_THRESHOLD ]; then
        echo "警告:CPU使用率超过${CPU_THRESHOLD}%,请及时检查系统进程!"
    else
        echo "CPU使用率正常(≤${CPU_THRESHOLD}%)"
    fi
    echo "--------------------------------------------------------"
}

# 函数4:监控内存使用率
monitor_mem() {
    echo "--------------------- 内存使用率监控 ---------------------"
    # free -h:人性化显示内存信息;grep Mem:筛选内存相关行;awk提取总内存($2)、已用内存($3)
    MEM_TOTAL=$(free -h | grep Mem | awk '{print $2}')
    MEM_USED=$(free -h | grep Mem | awk '{print $3}')
    # 计算内存使用率:已用内存/总内存 * 100,先转换为KB计算,避免小数误差
    MEM_USED_KB=$(free | grep Mem | awk '{print $3}')
    MEM_TOTAL_KB=$(free | grep Mem | awk '{print $2}')
    MEM_USAGE=$((MEM_USED_KB * 100 / MEM_TOTAL_KB))

    echo "当前内存使用情况:已用${MEM_USED} / 总${MEM_TOTAL}"
    echo "当前内存使用率:${MEM_USAGE}%"

    # 条件判断:若内存使用率超过阈值,输出警告
    if [ $MEM_USAGE -gt $MEM_THRESHOLD ]; then
        echo "警告:内存使用率超过${MEM_THRESHOLD}%,请及时释放内存!"
    else
        echo "内存使用率正常(≤${MEM_THRESHOLD}%)"
    fi
    echo "--------------------------------------------------------"
}

# 函数5:监控磁盘使用率
monitor_disk() {
    echo "--------------------- 磁盘使用率监控 ---------------------"
    # df -h:人性化显示磁盘信息;grep "/$":筛选根目录的磁盘信息;awk提取使用率($5),并去掉%符号
    DISK_USAGE=$(df -h | grep "/$" | awk '{print $5}' | cut -d% -f1)
    DISK_TOTAL=$(df -h | grep "/$" | awk '{print $2}')
    DISK_USED=$(df -h | grep "/$" | awk '{print $3}')

    echo "当前根目录磁盘使用情况:已用${DISK_USED} / 总${DISK_TOTAL}"
    echo "当前根目录磁盘使用率:${DISK_USAGE}%"

    # 条件判断:若磁盘使用率超过阈值,输出警告
    if [ $DISK_USAGE -gt $DISK_THRESHOLD ]; then
        echo "警告:磁盘使用率超过${DISK_THRESHOLD}%,请及时清理磁盘空间!"
    else
        echo "磁盘使用率正常(≤${DISK_THRESHOLD}%)"
    fi
    echo "--------------------------------------------------------"
}

# 函数6:将监控结果写入日志
write_log() {
    # 先获取当前时间,写入日志头部,区分每次监控记录
    echo "==================== 监控日志($(date +"%Y-%m-%d %H:%M:%S")) ====================" >> $LOG_PATH
    # 将系统基础信息写入日志
    get_system_info >> $LOG_PATH
    # 将CPU监控结果写入日志
    echo "--------------------- CPU使用率监控 ---------------------" >> $LOG_PATH
    echo "当前CPU使用率:${CPU_USAGE}%" >> $LOG_PATH
    if [ $CPU_USAGE -gt $CPU_THRESHOLD ]; then
        echo "警告:CPU使用率超过${CPU_THRESHOLD}%,请及时检查系统进程!" >> $LOG_PATH
    else
        echo "CPU使用率正常(≤${CPU_THRESHOLD}%)" >> $LOG_PATH
    fi
    # 将内存监控结果写入日志
    echo "--------------------- 内存使用率监控 ---------------------" >> $LOG_PATH
    echo "当前内存使用情况:已用${MEM_USED} / 总${MEM_TOTAL}" >> $LOG_PATH
    echo "当前内存使用率:${MEM_USAGE}%" >> $LOG_PATH
    if [ $MEM_USAGE -gt $MEM_THRESHOLD ]; then
        echo "警告:内存使用率超过${MEM_THRESHOLD}%,请及时释放内存!" >> $LOG_PATH
    else
        echo "内存使用率正常(≤${MEM_THRESHOLD}%)" >> $LOG_PATH
    fi
    # 将磁盘监控结果写入日志
    echo "--------------------- 磁盘使用率监控 ---------------------" >> $LOG_PATH
    echo "当前根目录磁盘使用情况:已用${DISK_USED} / 总${DISK_TOTAL}" >> $LOG_PATH
    echo "当前根目录磁盘使用率:${DISK_USAGE}%" >> $LOG_PATH
    if [ $DISK_USAGE -gt $DISK_THRESHOLD ]; then
        echo "警告:磁盘使用率超过${DISK_THRESHOLD}%,请及时清理磁盘空间!" >> $LOG_PATH
    else
        echo "磁盘使用率正常(≤${DISK_THRESHOLD}%)" >> $LOG_PATH
    fi
    # 写入空行,分隔每次监控记录,便于查看日志
    echo -e "\n" >> $LOG_PATH
}

# ====================== 主程序执行 ======================
# 1. 输出执行时间
get_current_time
# 2. 输出系统基础信息
get_system_info
# 3. 执行CPU、内存、磁盘监控
monitor_cpu
monitor_mem
monitor_disk
# 4. 将监控结果写入日志文件
write_log
# 5. 输出日志保存路径提示
echo "监控完成!监控结果已保存至日志文件:${LOG_PATH}"
echo "=================================================="

添加权限并执行脚本后,输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#添加执行权限
chmod +x system_monitor.sh

#执行脚本
./system_monitor.sh
#或指定日志保存路径
./system_monitor.sh /home/cat/system_monitor.log

#信息输出如下
==================== 执行时间:2026-03-03 09:26:12 ====================
--------------------- 系统基础信息 ---------------------
当前登录用户:cat
系统主机名:lubancat
系统内核版本:4.19.232
--------------------------------------------------------
--------------------- CPU使用率监控 ---------------------
当前CPU使用率:3%
CPU使用率正常(≤80%)
--------------------------------------------------------
--------------------- 内存使用率监控 ---------------------
当前内存使用情况:已用343Mi / 总1.9Gi
当前内存使用率:17%
内存使用率正常(≤80%)
--------------------------------------------------------
--------------------- 磁盘使用率监控 ---------------------
当前根目录磁盘使用情况:已用3.1G / 总29G
当前根目录磁盘使用率:11%
磁盘使用率正常(≤90%)
--------------------------------------------------------
监控完成!监控结果已保存至日志文件:./system_monitor.log
==================================================

保存的日志文件system_monitor.log内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
==================== 监控日志(2026-03-03 09:32:47) ====================
--------------------- 系统基础信息 ---------------------
当前登录用户:cat
系统主机名:lubancat
系统内核版本:4.19.232
--------------------------------------------------------
--------------------- CPU使用率监控 ---------------------
当前CPU使用率:7%
CPU使用率正常(≤80%)
--------------------- 内存使用率监控 ---------------------
当前内存使用情况:已用343Mi / 总1.9Gi
当前内存使用率:17%
内存使用率正常(≤80%)
--------------------- 磁盘使用率监控 ---------------------
当前根目录磁盘使用情况:已用3.1G / 总29G
当前根目录磁盘使用率:11%
磁盘使用率正常(≤90%)