从零开始学Shell编程:从基础到实战案例
从零开始学Shell编程:从基础到实战案例
文章目录
- 从零开始学Shell编程:从基础到实战案例
- 一、认识Shell:是什么与为什么学
- 1.1 Shell的定义
- 1.2 常用Shell解释器
- 二、Shell编程快速入门:编写第一个脚本
- 2.1 步骤1:创建脚本文件
- 2.2 步骤2:赋予执行权限
- 2.3 步骤3:执行脚本
- 三、Shell核心语法:变量与字符串
- 3.1 变量:定义、使用与修改
- 3.1.1 变量定义规则
- 3.1.2 变量使用
- 3.1.3 变量修改与删除
- 3.2 字符串:单引号、双引号与常用操作
- 3.2.1 三种字符串定义方式对比
- 3.2.2 字符串常用操作
- 四、流程控制:让脚本“有判断、会循环”
- 4.1 条件判断:if...else语句
- 4.1.1 语法格式
- 4.1.2 条件表达式
- 4.1.3 示例:成绩评级
- 4.2 循环执行:for与while语句
- 4.2.1 for循环:已知循环范围
- 4.2.2 while循环:按条件结束
- 五、实战案例:完整猜数字游戏
- 5.1 代码实现
在Linux系统操作中,Shell编程是提升效率的重要技能。它不仅能帮助我们自动化重复任务,还能整合各类命令实现复杂功能。本文将从Shell的基础概念讲起,逐步深入变量、字符串、运算符、流程控制、函数、数组等核心知识点,最后通过实战案例巩固所学,带你快速入门Shell编程。
一、认识Shell:是什么与为什么学
1.1 Shell的定义
Shell是用C语言编写的程序,它充当用户与操作系统内核之间的“桥梁”——用户通过Shell可以访问操作系统内核的服务。从功能上看,它类似Windows系统下的command
或cmd.exe
,但功能更强大、灵活性更高。
同时,Shell具有双重身份:
- 命令语言:可以直接在终端输入命令,执行文件操作、进程管理等任务;
- 程序设计语言:支持变量、函数、流程控制等编程特性,能编写脚本(Shell Script)实现自动化操作。
需要注意的是,“Shell编程”通常指编写Shell脚本,而非开发Shell解释器本身。
1.2 常用Shell解释器
Shell解释器是执行Shell脚本的核心工具,Linux系统中支持多种解释器,通过cat /etc/shells
命令可查看系统已安装的解释器。其中:
/bin/sh
:早期的Bourne Shell,功能较基础;/bin/bash
:Bourne Again Shell,兼容sh
且新增了大量特性(如命令补全、历史记录),是目前大多数Linux系统(如CentOS、Ubuntu)的默认Shell,也是本文的主要使用解释器;/sbin/nologin
:非交互Shell,通常用于禁止用户登录。
二、Shell编程快速入门:编写第一个脚本
学习Shell编程无需复杂的开发环境,只需一个文本编辑器(如vi
/vim
)和Shell解释器即可。下面以“打印Hello World”为例,带你完成第一个Shell脚本的编写与执行。
2.1 步骤1:创建脚本文件
使用vi
编辑器在指定目录(如/root/shelldemo
)创建脚本文件hello.sh
(.sh
是Shell脚本的约定后缀,不影响执行):
# 1. 创建存放脚本的目录(若不存在)
mkdir -p /root/shelldemo
# 2. 进入目录
cd /root/shelldemo
# 3. 用vi创建并编辑hello.sh
vi hello.sh
在vi
编辑器中输入以下内容:
#!/bin/bash
# 这是注释,打印Hello World
echo "Hello World!"
#!/bin/bash
:约定标记,告诉系统该脚本使用/bin/bash
解释器执行;#
:注释符号,后面的内容不会被执行;echo
:输出命令,用于向终端打印文本。
输入完成后,按Esc
键,输入:wq
保存并退出vi
。
2.2 步骤2:赋予执行权限
刚创建的脚本文件默认只有“读”和“写”权限,需要通过chmod
命令赋予“执行权限”:
# 给当前用户添加执行权限(+x表示新增执行权限,./hello.sh指定脚本路径)
chmod +x ./hello.sh
通过ls -l
查看权限是否生效,若输出中包含-rwxr-xr-x
,说明已成功添加执行权限:
ls -l hello.sh
# 输出示例:-rwxr-xr-x 1 root root 32 May 20 05:35 hello.sh
2.3 步骤3:执行脚本
Shell脚本有3种常见执行方式,可根据场景选择:
执行方式 | 命令示例 | 说明 |
---|---|---|
相对路径执行 | ./hello.sh | . 表示“当前目录”,系统会直接在当前目录寻找脚本并执行(需提前赋予执行权限) |
绝对路径执行 | /root/shelldemo/hello.sh | 直接指定脚本的完整路径,无需进入脚本所在目录(需执行权限) |
解释器参数执行 | sh hello.sh | 直接调用sh 解释器,将脚本作为参数传入(无需执行权限) |
执行结果如下:
# 相对路径执行
./hello.sh
# 输出:Hello World!# 解释器参数执行(即使无执行权限也能运行)
sh hello.sh
# 输出:Hello World!
注意:若直接输入hello.sh
执行,系统会在PATH
环境变量指定的目录中寻找脚本,若当前目录不在PATH
中,会提示“command not found”,因此需用./hello.sh
明确“当前目录”。
三、Shell核心语法:变量与字符串
变量和字符串是Shell编程的基础,掌握它们能帮你存储和处理数据。
3.1 变量:定义、使用与修改
Shell变量无需声明数据类型,直接通过“变量名=值”的格式定义,且等号两边不能有空格。
3.1.1 变量定义规则
- 变量名首字符必须是字母(a-z/A-Z);
- 变量名中间可包含下划线(_),但不能有空格或标点符号;
- 不能使用Shell关键字(如
if
、for
,可通过help
命令查看关键字)。
示例:
# 正确定义:变量名见名知意,多单词用下划线分隔
your_name="bigdata.com"
student_age=20# 错误定义:等号两边有空格、包含特殊字符
# your_name = "bigdata.com" (错误:等号有空格)
# student-age=20 (错误:包含减号)
3.1.2 变量使用
使用变量时,在变量名前加$
符号,若需明确变量边界,可加{}
(推荐):
your_name="bigdata.com"
# 两种使用方式,输出结果相同
echo $your_name
echo ${your_name}# 明确变量边界的场景:拼接字符串
echo "Hello, ${your_name}_user" # 输出:Hello, bigdata.com_user
# 若不加{}:echo "Hello, $your_name_user" 会被解析为“变量your_name_user”,导致错误
3.1.3 变量修改与删除
- 修改变量:直接重新赋值即可(普通变量支持修改);
- 只读变量:用
readonly
关键字定义,赋值后无法修改或删除; - 删除变量:用
unset
命令删除普通变量(无法删除只读变量)。
示例:
# 1. 修改普通变量
name="hadoop"
echo $name # 输出:hadoop
name="spark"
echo $name # 输出:spark# 2. 定义只读变量
readonly readonly_name="linux"
# readonly_name="ubuntu" # 错误:只读变量无法修改,执行会提示“readonly variable”# 3. 删除普通变量
unset name
echo $name # 输出为空(变量已删除)# unset readonly_name # 错误:无法删除只读变量
3.2 字符串:单引号、双引号与常用操作
字符串是Shell中最常用的数据类型,支持单引号、双引号和无引号三种定义方式,功能差异主要体现在“变量解析”和“转义字符”上。
3.2.1 三种字符串定义方式对比
定义方式 | 示例 | 特点 |
---|---|---|
单引号 | str='I love $your_name' | 1. 原样输出所有字符,不解析变量($your_name不会被替换为实际值); 2. 不能包含单独的单引号(即使转义也不行) |
双引号 | str="I love $your_name" | 1. 解析变量($your_name会被替换为实际值); 2. 支持转义字符(如 \n 换行、\" 双引号) |
无引号 | str=I love bigdata | 1. 解析变量,但不支持转义字符; 2. 字符串中不能有空格(否则会被视为多个参数) |
示例:
your_name="bigdata"
# 单引号:不解析变量
str1='I love $your_name'
echo $str1 # 输出:I love $your_name# 双引号:解析变量
str2="I love $your_name"
echo $str2 # 输出:I love bigdata# 双引号:支持转义字符
str3="Hello\nWorld"
echo -e $str3 # 输出:Hello(换行)World(-e启用转义)
3.2.2 字符串常用操作
Shell提供了便捷的字符串处理方法,如获取长度、提取子串、查找子串位置。
操作 | 语法 | 示例 | 结果 |
---|---|---|---|
获取长度 | ${#字符串} | skill="hadoop" echo ${#skill} | 6(hadoop共6个字符) |
提取子串 | ${字符串:起始索引:长度} | str="I am good at hadoop" echo ${str:2:2} (从索引2开始,取2个字符) | am(索引从0开始,空格算1个字符) |
查找子串位置 | expr index "$字符串" 子串 | str="I am good at hadoop" echo \ expr index “$str” am``(反引号包裹命令) | 3(am从第3个字符开始,位置从1算) |
注意:expr index
命令返回的是“子串中任意字符首次出现的位置”,而非完整子串的位置,若需精确查找完整子串,需结合其他命令(如grep
)。
四、流程控制:让脚本“有判断、会循环”
默认情况下,Shell脚本按“从上到下”顺序执行。通过流程控制语句,可实现“条件判断”和“循环执行”,让脚本更灵活。
4.1 条件判断:if…else语句
if...else
语句根据条件的“成立与否”(true/false)执行不同代码块,支持“单支”“双支”“多支”三种场景。
4.1.1 语法格式
- 单支:仅当条件成立时执行代码
if [ 条件 ]; then命令1 # 条件成立时执行 fi # 结束if语句(必须)
- 双支:条件成立/不成立分别执行不同代码
if [ 条件 ]; then命令1 # 条件成立 else命令2 # 条件不成立 fi
- 多支:多个条件依次判断,满足一个即执行
if [ 条件1 ]; then命令1 elif [ 条件2 ]; then命令2 ... else命令n # 所有条件不成立时执行 fi
4.1.2 条件表达式
条件判断依赖“条件表达式”,常见类型包括:
- 数字比较:用
-eq
(等于)、-gt
(大于)、-lt
(小于)等运算符(仅支持整数); - 字符串比较:用
=
(等于)、!=
(不等于)、-z
(空字符串)、-n
(非空字符串); - 文件判断:用
-f
(普通文件)、-d
(目录)、-e
(文件存在)等。
4.1.3 示例:成绩评级
根据输入的成绩(0-100)输出评级(优秀/良好/及格/不及格):
# 创建脚本grade.sh
vi grade.sh
输入内容:
#!/bin/bash
score=$1 # $1表示执行脚本时传入的第一个参数
if [ $score -ge 90 ]; thenecho "优秀"
elif [ $score -ge 80 ]; thenecho "良好"
elif [ $score -ge 60 ]; thenecho "及格"
elseecho "不及格"
fi
执行脚本并传入参数(如“85”):
chmod +x grade.sh
./grade.sh 85 # 输出:良好
./grade.sh 59 # 输出:不及格
4.2 循环执行:for与while语句
循环用于“重复执行某段代码”,Shell中常用for
和while
循环,分别适用于“已知循环次数”和“未知循环次数(按条件结束)”的场景。
4.2.1 for循环:已知循环范围
for
循环有两种常用格式:
格式 | 适用场景 | 示例(打印1-5) |
---|---|---|
数值循环 | 已知起始值、结束值、步长 | for ((i=1; i<=5; i++)); do echo $i; done |
列表循环 | 遍历已知列表(如数组、文件列表) | for i in 1 2 3 4 5; do echo $i; done |
示例1:计算1-10的累加和
vi sum_for.sh
输入内容:
#!/bin/bash
sum=0 # 存储累加结果
# 数值循环:i从1到10,每次+1
for ((i=1; i<=10; i++)); dosum=$((sum + i)) # $((...))用于算术运算
done
echo "1-10的累加和:$sum" # 输出:55
示例2:遍历目录下的文件
打印/root/shelldemo
目录下的所有文件名:
vi list_files.sh
输入内容:
#!/bin/bash
# 列表循环:遍历ls命令的结果(即目录下的文件)
for filename in $(ls /root/shelldemo); doecho "文件名:$filename"
done
4.2.2 while循环:按条件结束
while
循环通过“条件是否成立”控制循环,适用于“未知循环次数”的场景(如等待用户输入正确值)。
语法格式:
while [ 条件 ]; do命令1修改条件 # 避免死循环
done
示例:猜数字游戏(简化版)
程序内置1-10的随机数,用户猜数字,直到猜对为止:
vi guess_simple.sh
输入内容:
#!/bin/bash
# 生成1-10的随机数(RANDOM是系统变量,0-32767)
num=$[RANDOM%10+1]
while true; do # true表示无限循环,需在内部用exit退出read -p "猜一个1-10的数字:" caiif [ $cai -eq $num ]; thenecho "恭喜猜对了!"exit # 猜对后退出循环elif [ $cai -gt $num ]; thenecho "猜大了,再试试!"elseecho "猜小了,再试试!"fi
done
执行脚本,体验交互过程:
chmod +x guess_simple.sh
./guess_simple.sh
# 输出示例:
# 猜一个1-10的数字:5
# 猜小了,再试试!
# 猜一个1-10的数字:8
# 恭喜猜对了!
五、实战案例:完整猜数字游戏
结合前面所学的变量、流程控制、随机数生成等知识,我们来实现一个“1-100的猜数字游戏”,规则如下:
- 程序随机生成1-100的整数;
- 用户每次输入一个数字,系统提示“大了”“小了”或“猜对了”;
- 猜对后提示“游戏结束”,并退出程序。
5.1 代码实现
创建脚本guess_number.sh
:
vi guess_number.sh
输入内容:
#!/bin/bash
# 猜数字游戏:1-100的随机数,直到猜对为止# 1. 生成1-100的随机数(RANDOM%100得到0-99,+1后为1-100)
target_num=$[RANDOM%100+1]# 2. 无限循环,等待用户输入
while true; do# 接收用户输入(-p提示用户输入内容)read -p "计算机生成了1-100的随机数,请猜:" user_guess# 3. 判断输入是否为整数(避免非数字输入导致脚本报错)if ! [[ $user_guess =~ ^[0-9]+$ ]]; thenecho "请输入有效的整数