从零开始学Shell编程:从基础到实战案例

发布于:2025-08-31 ⋅ 阅读:(30) ⋅ 点赞:(0)

从零开始学Shell编程:从基础到实战案例


在Linux系统操作中,Shell编程是提升效率的重要技能。它不仅能帮助我们自动化重复任务,还能整合各类命令实现复杂功能。本文将从Shell的基础概念讲起,逐步深入变量、字符串、运算符、流程控制、函数、数组等核心知识点,最后通过实战案例巩固所学,带你快速入门Shell编程。

一、认识Shell:是什么与为什么学

1.1 Shell的定义

Shell是用C语言编写的程序,它充当用户与操作系统内核之间的“桥梁”——用户通过Shell可以访问操作系统内核的服务。从功能上看,它类似Windows系统下的commandcmd.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关键字(如iffor,可通过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 ]; then
    echo "优秀"
elif [ $score -ge 80 ]; then
    echo "良好"
elif [ $score -ge 60 ]; then
    echo "及格"
else
    echo "不及格"
fi

执行脚本并传入参数(如“85”):

chmod +x grade.sh
./grade.sh 85  # 输出:良好
./grade.sh 59  # 输出:不及格

在这里插入图片描述
在这里插入图片描述

4.2 循环执行:for与while语句

循环用于“重复执行某段代码”,Shell中常用forwhile循环,分别适用于“已知循环次数”和“未知循环次数(按条件结束)”的场景。

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++)); do
    sum=$((sum + i))  # $((...))用于算术运算
done
echo "1-10的累加和:$sum"  # 输出:55

示例2:遍历目录下的文件
打印/root/shelldemo目录下的所有文件名:

vi list_files.sh

输入内容:

#!/bin/bash
# 列表循环:遍历ls命令的结果(即目录下的文件)
for filename in $(ls /root/shelldemo); do
    echo "文件名:$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的数字:" cai
    if [ $cai -eq $num ]; then
        echo "恭喜猜对了!"
        exit  # 猜对后退出循环
    elif [ $cai -gt $num ]; then
        echo "猜大了,再试试!"
    else
        echo "猜小了,再试试!"
    fi
done

执行脚本,体验交互过程:

chmod +x guess_simple.sh
./guess_simple.sh
# 输出示例:
# 猜一个1-10的数字:5
# 猜小了,再试试!
# 猜一个1-10的数字:8
# 恭喜猜对了!

五、实战案例:完整猜数字游戏

结合前面所学的变量、流程控制、随机数生成等知识,我们来实现一个“1-100的猜数字游戏”,规则如下:

  1. 程序随机生成1-100的整数;
  2. 用户每次输入一个数字,系统提示“大了”“小了”或“猜对了”;
  3. 猜对后提示“游戏结束”,并退出程序。

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]+$ ]]; then
        echo "请输入有效的整数

网站公告

今日签到

点亮在社区的每一天
去签到