Linux操作系统-shell编程(0)

Linux Shell 编程实战:让自动化替你"搬砖"

引言

如果你是一个运维工程师,每天可能要重复执行几十次同样的命令------登录服务器、查日志、清缓存、重启服务......这些重复劳动不仅枯燥,而且容易出错。

如果你是一个开发人员,每次部署都要手动敲一串命令,环境稍微有点差异就报错,排错半天。

如果你是一个刚接触 Linux 的新手,面对黑乎乎的终端,不知道从哪里下手。

其实,这些问题都有一个共同的解决方案------Shell 脚本

Shell 脚本本质上是 Linux 命令的"批处理"文件。把你平时手动敲的那些命令按顺序写到一个文件里,加上一点逻辑判断和循环,它就变成了一个自动化工具。一次编写,永久复用 。可以说Shell脚本是Linux操作系统的精华,不懂Shell那就没掌握Linux操作系统运维的精髓

今天这篇文章,我们就来系统学习 Linux Shell 编程:从基础语法到流程控制,从函数封装到调试技巧,再到几个拿来就能用的实战案例。读完这篇,你就能写出自己的第一个生产级脚本。

一、Shell 是什么?为什么值得学?

Shell 是用户与 Linux 内核之间的桥梁。你在终端里敲的每一个命令,都是 Shell 在帮你解释并调用内核执行。

Shell 不仅仅是一个命令解释器,它本身也是一门功能相当强大的编程语言,易编写、易调试、灵活性强。

Shell 脚本的核心价值在于自动化

  • 自动化处理:把重复性操作写成脚本,一键执行
  • 批量管理:通过循环结构同时管理数百台设备
  • 资源高效:低内存占用,适合服务器环境
  • 灵活定制:可组合命令构建复杂逻辑

常见的 Shell 类型包括 Bash(最主流,Linux 默认)、Zsh、Ksh 等。Bash 凭借强大的脚本功能和广泛的社区支持,成为生产环境的首选。

💡 一个真实的效率对比:手动操作,每小时仅能处理 5-10 台服务器;而通过 Shell 脚本,单台服务器操作时间可压缩至秒级,配合循环结构可同时管理数百台设备,效率提升数十倍。

二、第一个 Shell 脚本:Hello World

在开始之前,我们先写一个最简单的脚本,感受一下流程。

第一步:创建脚本文件

bash 复制代码
vim hello.sh

第二步:写入内容

bash 复制代码
#!/bin/bash
# 这是我的第一个 Shell 脚本
echo "Hello, World!"
  • #!/bin/bashshebang,告诉系统用哪个解释器来执行这个脚本
  • # 开头的行是注释,不会被执行
  • echo 是输出命令

第三步:赋予执行权限并运行

bash 复制代码
chmod +x hello.sh
./hello.sh

输出:

复制代码
Hello, World!

💡 小贴士 :脚本也可以用 bash hello.shsh hello.sh 直接运行,无需执行权限。

三、变量:脚本的"记忆单元"

变量是编程语言的基石。Shell 中的变量无需声明类型,直接赋值即可使用

3.1 变量定义与引用

bash 复制代码
# 定义变量(等号两侧不能有空格!)
name="Linux Shell"      # 字符串
count=100               # 数字(Shell 中默认也是字符串)
pi=3.14                 # 浮点数

# 引用变量(加 $ 符号)
echo $name
echo ${name}            # 推荐用花括号,避免歧义

关键规则

  • 等号两侧不能有空格
  • 变量名不能以数字开头
  • 可以使用下划线,不能使用破折号或标点符号
  • 变量引用需加 $ 符号
  • 推荐使用 ${var} 而非 $var,避免歧义

3.2 单引号 vs 双引号

字符串可以用单引号或双引号,区别很大:

bash 复制代码
name="Alice"
echo 'Hello, $name'   # 输出:Hello, $name(原样输出,变量不解析)
echo "Hello, $name"   # 输出:Hello, Alice(变量被解析)

规则

  • 单引号:原样输出,里面的变量和转义字符都不生效
  • 双引号:变量会被解析,转义字符生效

3.3 特殊变量(非常重要!)

Shell 提供了一些内置的特殊变量:

变量 含义
$0 当前脚本的名称
$1~$9 脚本的第 1~9 个参数
$# 传递的参数个数
$* 所有参数(作为一个整体字符串)
$@ 所有参数(每个作为独立字符串)
$? 上一条命令的退出状态码(0=成功,非0=失败)
$$ 当前 Shell 的进程 ID

示例

bash 复制代码
#!/bin/bash
echo "脚本名: $0"
echo "第一个参数: $1"
echo "参数个数: $#"
echo "所有参数: $@"

运行:

bash 复制代码
./test.sh hello world
# 输出:
# 脚本名: ./test.sh
# 第一个参数: hello
# 参数个数: 2
# 所有参数: hello world

3.4 数组

Shell 支持数组,下标从 0 开始:

bash 复制代码
# 定义数组
fruits=("apple" "banana" "orange")

# 访问单个元素
echo ${fruits[0]}      # 输出:apple

# 访问所有元素
echo ${fruits[@]}      # 输出:apple banana orange

# 获取数组长度
echo ${#fruits[@]}     # 输出:3

# 遍历数组
for fruit in "${fruits[@]}"; do
    echo "I like $fruit"
done

四、流程控制:让脚本有"脑子"

有了变量只能存数据,有了流程控制,脚本才能做判断、走分支、循环执行------这才是编程语言的灵魂。

4.1 if 条件判断

if 语句支持数值、字符串、文件状态等多种判断。

基本语法

bash 复制代码
if [ 条件 ]; then
    # 条件成立时执行
elif [ 另一个条件 ]; then
    # 另一个条件成立时执行
else
    # 所有条件都不成立时执行
fi

常见条件测试

测试类型 运算符 含义
数值比较 -eq 等于
-ne 不等于
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
文件测试 -f 存在且为普通文件
-d 存在且为目录
-e 文件存在
-x 文件可执行
字符串 = 相等
!= 不等
-z 为空

实战示例

bash 复制代码
#!/bin/bash

# 数值比较
a=10
if [ $a -gt 5 ]; then
    echo "$a 大于 5"
fi

# 文件存在性检查
if [ -f "/etc/passwd" ]; then
    echo "passwd 文件存在"
else
    echo "passwd 文件不存在"
fi

# 字符串匹配(推荐使用 [[ ]])
if [[ "$name" == *"error"* ]]; then
    echo "包含错误信息"
fi

💡 推荐 :使用 [[ ]] 替代 [ ],前者支持更丰富的模式匹配(如通配符 *)。

4.2 case 多分支选择

当有多个分支时,case 比多个 if-elif 更清晰:

bash 复制代码
#!/bin/bash

case $1 in
    start)
        echo "启动服务..."
        ;;
    stop)
        echo "停止服务..."
        ;;
    restart)
        echo "重启服务..."
        ;;
    *)
        echo "用法: $0 {start|stop|restart}"
        ;;
esac

4.3 for 循环

for 循环用于遍历列表或范围:

bash 复制代码
# 遍历列表
for file in log1.txt log2.txt log3.txt; do
    echo "处理文件: $file"
done

# 遍历数组
files=("log1.txt" "log2.txt" "log3.txt")
for file in "${files[@]}"; do
    echo "处理文件: $file"
done

# 数字序列循环
for i in {1..5}; do
    echo "第 $i 次循环"
done

# C 语言风格
for ((i=1; i<=5; i++)); do
    echo "第 $i 次循环"
done

实战:批量重命名文件

bash 复制代码
# 将所有 .txt 文件重命名为 .backup
for file in *.txt; do
    mv "$file" "${file%.txt}.backup"
done

4.4 while 循环

while 循环适合不确定次数的场景,条件为真时持续执行:

bash 复制代码
#!/bin/bash

count=5
while [ $count -gt 0 ]; do
    echo "剩余次数: $count"
    count=$((count - 1))
done

4.5 until 循环

until 和 while 相反,条件为假时执行,直到条件变为真:

bash 复制代码
count=1
until [ $count -gt 5 ]; do
    echo "计数: $count"
    count=$((count + 1))
done

4.6 循环控制:break 和 continue

bash 复制代码
# break:跳出整个循环
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        break
    fi
    echo $i
done
# 输出:1 2 3 4

# continue:跳过本次循环
for i in {1..5}; do
    if [ $i -eq 3 ]; then
        continue
    fi
    echo $i
done
# 输出:1 2 4 5

五、函数:让代码可复用

函数是模块化编程的核心。把重复的逻辑封装成函数,脚本更清晰、更易维护。

5.1 定义与调用

bash 复制代码
# 定义函数(两种写法等价)
function greet() {
    echo "Hello, $1!"
}

# 或者
greet() {
    echo "Hello, $1!"
}

# 调用函数(不加括号)
greet "World"    # 输出:Hello, World!

5.2 函数参数与返回值

函数通过 $1$2 等位置变量接收参数。返回值用 return 指定(0-255),$? 获取:

bash 复制代码
#!/bin/bash

# 带参数的函数
backup_file() {
    local src=$1
    local dest=$2
    cp "$src" "$dest" && echo "备份成功: $dest" || echo "备份失败"
}

# 带返回值的函数
check_number() {
    if [ $1 -gt 0 ]; then
        return 0   # 表示成功/真
    else
        return 1   # 表示失败/假
    fi
}

# 调用
backup_file "/var/log/syslog" "/backup/syslog.log"
check_number 5
if [ $? -eq 0 ]; then
    echo "是正数"
fi

5.3 局部变量

local 关键字声明的变量只在函数内部有效,不会污染全局:

bash 复制代码
#!/bin/bash

my_func() {
    local var="我是局部变量"
    echo "函数内: $var"
}

my_func
echo "函数外: $var"   # 输出为空,因为 var 是局部变量

六、调试技巧:让 Bug 无处遁形

Shell 脚本的调试是新手最容易卡住的地方。下面介绍几种实用的调试方法。

6.1 echo 大法(最基础)

在关键位置打印变量值,是最简单直接的调试方式:

bash 复制代码
#!/bin/bash
name="Alice"
echo "DEBUG: name=$name"   # 打印变量值

6.2 set -x:跟踪执行(最推荐)

set -x 会让 Shell 在执行每条命令前,把命令打印出来:

bash 复制代码
#!/bin/bash
set -x   # 开启调试模式

name="Alice"
echo "Hello, $name"

set +x   # 关闭调试模式

输出会显示每一条命令及其展开后的结果,非常适合定位逻辑错误。

6.3 set -e:遇错即停

默认情况下,脚本中某条命令失败后会继续执行,可能导致更严重的连锁问题。set -e 让脚本在遇到任何错误时立即退出:

bash 复制代码
#!/bin/bash
set -e   # 任何命令失败就退出

rm /nonexistent/file   # 这行会报错,脚本立即退出
echo "这行不会执行"

6.4 组合技:set -euxo pipefail

这是生产环境脚本的黄金组合

bash 复制代码
#!/bin/bash
set -euxo pipefail

各选项含义:

  • -e:命令失败立即退出
  • -u:使用未定义变量时报错
  • -x:打印执行的每条命令
  • -o pipefail:管道中任何命令失败都算失败

6.5 trap:捕获错误与信号

trap 可以在脚本被中断或出错时执行清理操作:

bash 复制代码
#!/bin/bash

cleanup() {
    echo "脚本被终止,执行清理..."
    rm -f /tmp/temp_file
}

# 捕获 Ctrl+C 和脚本错误
trap cleanup SIGINT SIGTERM ERR

# 正常逻辑...

6.6 语法检查:bash -n

不执行脚本,仅检查语法错误:

bash 复制代码
bash -n script.sh

七、实战案例:拿来就能用

案例一:批量检查服务器存活

bash 复制代码
#!/bin/bash
# check_servers.sh - 批量 Ping 检测服务器存活

servers=("192.168.1.1" "192.168.1.2" "192.168.1.3")

for ip in "${servers[@]}"; do
    if ping -c 1 -W 1 $ip > /dev/null 2>&1; then
        echo "✅ $ip 存活"
    else
        echo "❌ $ip 不可达"
    fi
done

案例二:Nginx 服务管理脚本

bash 复制代码
#!/bin/bash
# nginx_ctl.sh - Nginx 服务管理

NGINX_HOME="/usr/local/nginx"
NGINX_PID="$NGINX_HOME/logs/nginx.pid"

start() {
    if [ -f $NGINX_PID ] && kill -0 $(cat $NGINX_PID) 2>/dev/null; then
        echo "Nginx 已经在运行"
    else
        $NGINX_HOME/sbin/nginx
        echo "Nginx 启动成功"
    fi
}

stop() {
    if [ -f $NGINX_PID ]; then
        kill -QUIT $(cat $NGINX_PID)
        echo "Nginx 已停止"
    else
        echo "Nginx 未运行"
    fi
}

reload() {
    if [ -f $NGINX_PID ]; then
        kill -HUP $(cat $NGINX_PID)
        echo "Nginx 配置已重载"
    else
        echo "Nginx 未运行"
    fi
}

case $1 in
    start)   start ;;
    stop)    stop ;;
    reload)  reload ;;
    restart) stop && sleep 1 && start ;;
    *)       echo "用法: $0 {start|stop|restart|reload}" ;;
esac

案例三:磁盘使用率监控告警

bash 复制代码
#!/bin/bash
# disk_monitor.sh - 磁盘使用率监控

THRESHOLD=80
EMAIL="admin@example.com"

df -h | grep -vE '^Filesystem|tmpfs|devtmpfs' | while read line; do
    usage=$(echo $line | awk '{print $5}' | sed 's/%//')
    mount=$(echo $line | awk '{print $6}')
    if [ $usage -gt $THRESHOLD ]; then
        echo "警告: $mount 使用率已达 $usage%" | mail -s "磁盘告警" $EMAIL
    fi
done

八、生产环境最佳实践

8.1 脚本头部模板

bash 复制代码
#!/bin/bash
#-------------------------------------------------------------------
# 脚本名称: xxx.sh
# 功能描述: xxx
# 作者: xxx
# 版本: 1.0
# 创建日期: $(date +%Y-%m-%d)
#-------------------------------------------------------------------
set -euo pipefail

8.2 使用绝对路径

在脚本中尽量使用绝对路径,避免因 PATH 环境变量不同导致命令找不到。

8.3 变量加引号

变量引用时始终加双引号,避免空格或特殊字符导致意外解析:

bash 复制代码
# ❌ 不推荐
file=$1
rm $file

# ✅ 推荐
file="$1"
rm "$file"

8.4 检查命令是否存在

bash 复制代码
if ! command -v nginx &> /dev/null; then
    echo "nginx 命令不存在,请先安装"
    exit 1
fi

8.5 函数封装,避免重复

重复的逻辑封装成函数,提高可维护性。

8.6 日志记录

bash 复制代码
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> /var/log/my_script.log
}

log "脚本开始执行"

总结

Shell 编程是 Linux 运维和自动化领域的核心技能。从基础语法到流程控制,从函数封装到调试技巧,掌握这些知识,你就能把日常的重复劳动交给脚本去完成。

记住三句话

  1. Shell 是人与系统交互的桥梁:掌握它,你就掌握了 Linux 的"遥控器"。
  2. 脚本的本质是命令的自动化组合:把重复的事写成脚本,让机器替你"搬砖"。
  3. 先能跑,再优化:不要追求一开始就写出完美的脚本,先写出能用的,再逐步完善。

Shell 脚本的威力在于------一次编写,永久复用;一处编写,处处运行。希望这篇文章能帮你迈出 Shell 编程的第一步,写出属于你自己的自动化工具!

本篇文章主要是让读者对shell编程有一个大致的认识和了解,后续将带您更加深入的学习shell编程的相关知识,如果你有任何问题或经验分享,欢迎在评论区留言讨论!

相关推荐
W658034191 小时前
2026年AI编程工具实测横评:Claude Code v2.1、Cursor 3.0、Trae SOLO、Copilot、Windsurf 谁更好用?
python·copilot·ai编程
snow@li1 小时前
技术栈对应:Vue (前端) + SpringBoot (Java 后端) =》 Python 全场景配套
python
2301_777998341 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器
yunwei371 小时前
eBPF 教程:检查 exec 后真正安装的可执行镜像
linux·开源
2501_909509101 小时前
DAY 28
开发语言·python
码云骑士1 小时前
71-Agent记忆系统-短期记忆-长期记忆-向量知识库三层架构
python·架构
卷无止境1 小时前
Python 的 exec 与 eval :动态代码执行的能力、风险与工程实践
后端·python
user-猴子1 小时前
从零构建 2048 游戏,解析“Python-Use”范式的完整闭环
开发语言·python·游戏
郝学胜_神的一滴2 小时前
Python 高级编程 025:二分利器bisect模块:优雅维系有序序列,极致优化检索性能
python·pycharm