尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Shell脚本编程实战:从入门到精通,掌握自动化运维核心技能

Shell脚本编程实战:从入门到精通,掌握自动化运维核心技能 这次我们来看一套完整的 Shell 脚本编程实战教程。对于 Linux 运维、开发或任何需要与服务器打交道的工程师来说Shell 脚本是绕不开的核心技能。它不是什么高深莫测的黑科技而是一把能极大提升工作效率的“瑞士军刀”——从批量处理文件、自动化部署到监控系统状态、处理日志几乎无处不在。这套教程的重点不是空谈概念而是直接上手实战覆盖从变量定义、流程控制循环/判断到函数封装和文本处理“三剑客”grep, sed, awk的全套核心内容。无论你是刚接触 Linux 的新手还是想系统化梳理脚本知识的运维都能从这里找到可立即运行的代码示例和清晰的解决思路。本文将带你快速建立 Shell 脚本的知识框架并通过一系列具体场景的实战案例让你理解脚本能解决什么问题、编写时有哪些“坑”、以及如何写出健壮、可维护的脚本。我们会从最基础的脚本创建与执行讲起逐步深入到变量、循环、条件判断、函数最后攻克文本处理的“三剑客”。每个环节都配有可直接复制的代码和常见问题排查方法。1. 核心能力速览在深入细节之前我们先通过下表快速了解 Shell 脚本编程的核心价值与能力边界这有助于你判断是否值得投入时间学习以及它能帮你解决哪类问题。能力项说明与定位学习门槛低。具备基础 Linux 命令使用经验即可开始。环境要求极低。任何主流 Linux 发行版Ubuntu, CentOS, AlmaLinux等或 macOS 终端自带Windows 可通过 WSL、Git Bash、Cygwin 获得兼容环境。核心功能自动化执行将一系列手动命令组合成脚本自动运行。任务调度结合 crontab 实现定时任务。文本处理使用 grep, sed, awk 高效分析日志、配置文件等。系统管理批量管理用户、进程、文件、软件包等。流程封装将复杂操作封装成函数或脚本简化操作。主要应用场景日常运维日志切割、备份、监控、CI/CD 流水线中的任务脚本、数据处理文本提取、格式转换、开发环境初始化、批量测试等。不适合的场景高性能计算、复杂图形界面应用、大型业务系统开发应使用 Python/Go/Java等。输出成果可执行的.sh脚本文件通过命令解释器如 Bash运行。2. 适用场景与使用边界Shell 脚本是一把锋利的刀用对场景事半功倍用错场景则可能事倍功半甚至带来风险。最适合 Shell 脚本解决的几类问题文件与目录批量操作例如递归查找并删除特定格式的临时文件批量重命名或移动大量文件统计目录大小等。文本日志分析与提取这是 Shell 的“主场”。从庞大的应用日志中快速过滤错误信息、统计访问频次、提取特定时间段的数据grep,awk,sed组合使用效率极高。系统状态监控与告警编写监控脚本定期检查 CPU、内存、磁盘使用率或检查特定服务进程是否存活异常时发送邮件或调用告警接口。自动化部署与备份将软件安装、配置更新、文件备份、数据库导出等一系列步骤固化到脚本中实现一键操作。胶水脚本将不同语言如 Python, Java编写的程序、或不同的系统命令串联起来协调工作。需要谨慎或避免使用 Shell 脚本的场景复杂的数学运算或数据结构Shell 对浮点数运算、数组操作支持较弱此类任务应交给 Python、Perl 等。对性能要求极高的处理Shell 脚本是解释执行的启动进程开销大不适合处理海量数据或要求低延迟的任务。涉及复杂网络通信或API调用虽然可以用curl发起简单请求但处理复杂的 HTTP 状态、JSON 解析、重试机制等用专业语言更稳妥。需要跨平台高度兼容不同 Unix-like 系统甚至不同版本的 Bash的 Shell 语法和行为可能有细微差异。编写时必须注意的风险与边界权限风险脚本中可能包含rm -rf、chmod、sudo等危险命令务必在关键操作前添加确认提示或实施“干运行”dry-run模式。路径依赖避免在脚本中使用绝对路径应使用相对路径或通过变量定义路径提高可移植性。输入验证如果脚本接受外部参数必须对其进行验证和清理防止命令注入攻击。错误处理默认情况下脚本会忽略错误继续执行。必须使用set -e、set -u等选项或显式检查命令返回值 ($?)确保失败时能及时终止或告警。3. 环境准备与前置条件开始编写和运行 Shell 脚本你只需要一个可用的 Shell 环境。以下是通用的准备清单操作系统与环境Linux任何主流发行版均可终端应用如 GNOME Terminal, Konsole开箱即用。macOS打开“终端”应用即可。Windows首选安装 WSL (Windows Subsystem for Linux) 获得原生 Linux 环境。备选安装 Git for Windows使用其附带的 Git Bash 终端。旧方案Cygwin 或 MSYS2。Shell 解释器绝大多数现代 Linux 和 macOS 的默认 Shell 是Bash (Bourne-Again SHell)。本教程以 Bash 为主要语法标准。可以通过命令echo $SHELL或echo $0查看当前使用的 Shell。确保 Bash 版本较新一般系统自带即可如需升级可参考系统包管理器。文本编辑器任何纯文本编辑器都可编写脚本如vim,nano,emacs。推荐使用具备语法高亮和代码提示的编辑器或 IDE如Visual Studio Code安装 Shell 相关插件、Sublime Text、JetBrains Fleet等能显著提升编写效率和减少语法错误。基础命令知识需要熟悉最基本的 Linux 命令如ls,cd,cp,mv,rm,cat,echo,ps,grep等。如果还不熟悉建议先简单了解。4. 第一个 Shell 脚本创建、执行与调试理论说再多不如动手跑一行。我们从创建一个最简单的脚本开始并理解如何执行它。4.1 创建与执行脚本创建脚本文件 打开终端使用vim、nano或直接在编辑器中创建一个新文件例如hello.sh。# 使用 nano 编辑器创建并编辑 nano hello.sh编写脚本内容 在第一行必须指定脚本的解释器这被称为shebang。对于 Bash通常是#!/bin/bash。然后写下你的命令。#!/bin/bash # 这是一个注释打印 Hello World echo Hello, Shell World! # 显示当前日期 date赋予执行权限 新创建的文件默认没有执行权限。使用chmod命令添加权限。chmod x hello.sh这条命令给当前用户添加了执行(x)权限。你可以使用ls -l hello.sh查看权限变化。执行脚本 有三种常见方式执行脚本# 方法1使用绝对路径或相对路径需要执行权限 ./hello.sh # 方法2使用 bash 解释器直接执行无需文件有执行权限 bash hello.sh # 方法3使用 source 或 . 命令在当前Shell环境中执行 source hello.sh # 或 . hello.sh重要区别./和bash会启动一个新的子Shell来运行脚本脚本中定义的变量在脚本结束后会消失。而source或.是在当前Shell中执行脚本中定义的变量和函数会保留在当前Shell环境中。4.2 脚本调试技巧编写脚本难免出错掌握调试方法至关重要。语法检查使用bash -n可以检查脚本语法错误而不执行。bash -n your_script.sh如果没有输出则表示语法正确。详细执行过程使用bash -x可以跟踪脚本的执行过程打印出每一行命令及其展开后的参数。bash -x your_script.sh这对于理解脚本逻辑、查找变量展开错误非常有用。在脚本中启用调试你也可以在脚本内部启用调试。#!/bin/bash set -x # 从此处开始打印执行轨迹 echo Start debugging... your_command set x # 关闭执行轨迹打印 set -e # 遇到任何命令失败返回值非0立即退出脚本 set -u # 遇到未定义的变量时立即退出脚本建议在重要的脚本开头加上set -euo pipefail这是一个提高脚本健壮性的常见组合-o pipefail表示管道中任何一个命令失败都视为整个管道失败。5. Shell 脚本核心语法实战5.1 变量数据的容器变量是脚本编程的基石用于存储各种数据。#!/bin/bash # 定义变量等号两边不能有空格 nameShell Learner count100 files_list$(ls) # 将命令执行结果赋值给变量 # 使用变量使用 $ 符号引用 echo Hello, $name # 输出: Hello, Shell Learner echo Count is: $count # 花括号 {} 用于明确变量边界 echo I have ${count}books. # 正确 # echo I have $countbooks. # 错误会尝试寻找变量 countbooks # 只读变量 readonly readonly_varCant Change # readonly_varnew value # 执行此行会报错 # 删除变量不能删除只读变量 unset count echo Count after unset: $count # 输出空行 # 环境变量当前Shell及其子进程可用 export MY_ENV_VARIm an environment variable bash -c echo $MY_ENV_VAR # 在子Shell中能访问到 # 特殊变量 echo Script name: $0 # 脚本文件名 echo First argument: $1 # 第一个参数 echo All arguments: $ # 所有参数列表 echo Number of arguments: $# # 参数个数 echo Last command exit status: $? # 上一个命令的退出状态0表示成功 echo Current process ID: $$ # 当前脚本的进程ID常见坑点变量赋值等号两边不能有空格变量引用时如果变量名后面紧跟字母、数字或下划线必须用{}括起来。5.2 传递参数让脚本更灵活脚本可以接收外部传入的参数使其行为动态化。#!/bin/bash # 假设脚本名为 deploy.sh执行: ./deploy.sh prod app-server 8080 environment$1 # 第一个参数值为 prod service_name$2 # 第二个参数值为 app-server port${3:-8080} # 第三个参数如果未提供则默认为 8080 echo Deploying service [$service_name] to [$environment] environment, port: $port # 遍历所有参数 echo All parameters: for arg in $; do echo - $arg done # 参数个数检查 if [ $# -lt 2 ]; then echo Usage: $0 environment service_name [port] exit 1 # 非0退出码表示错误 fi5.3 数组管理一组数据Bash 支持一维数组用于存储多个值。#!/bin/bash # 数组定义 fruits(Apple Banana Orange Grape) # 访问数组元素 echo First fruit: ${fruits[0]} # 索引从0开始 echo All fruits: ${fruits[]} # 获取所有元素 echo Number of fruits: ${#fruits[]} # 数组长度 # 遍历数组 for fruit in ${fruits[]}; do echo I like $fruit done # 追加元素 fruits(Mango) echo After append: ${fruits[]} # 关联数组 (Bash 4.0 支持需要先声明) declare -A user_info user_info[name]Alice user_info[age]25 user_info[city]Beijing echo User name: ${user_info[name]}5.4 条件判断控制执行流程使用if、case语句让脚本根据条件做出决策。#!/bin/bash # if 语句基本结构 read -p Enter a number: num if [ $num -gt 100 ]; then echo The number is greater than 100. elif [ $num -eq 100 ]; then echo The number is exactly 100. else echo The number is less than 100. fi # 文件测试 file_path./hello.sh if [ -f $file_path ]; then echo $file_path is a regular file. fi if [ -x $file_path ]; then echo $file_path is executable. fi # 字符串比较 read -p Enter yes or no: answer if [[ $answer yes ]]; then # 使用双中括号 [[ ]] 支持更强大的匹配 echo You agreed. elif [[ $answer ~ ^[Nn]o?$ ]]; then # 正则匹配 echo You disagreed. else echo Invalid input. fi # case 语句适合多分支匹配 read -p Enter a command (start|stop|restart|status): cmd case $cmd in start) echo Starting service... # systemctl start some-service ;; stop) echo Stopping service... # systemctl stop some-service ;; restart) echo Restarting service... # systemctl restart some-service ;; status) echo Checking service status... # systemctl status some-service ;; *) echo Unknown command: $cmd echo Usage: $0 {start|stop|restart|status} exit 1 ;; esac条件判断常用操作符数值比较-eq(等于),-ne(不等于),-gt(大于),-lt(小于),-ge(大于等于),-le(小于等于)。字符串比较或(相等),!(不相等),-z(字符串长度为0),-n(字符串长度非0)。文件测试-e(存在),-f(是普通文件),-d(是目录),-r(可读),-w(可写),-x(可执行),-s(文件大小非空)。5.5 循环重复执行的艺术循环用于处理列表数据或重复操作。#!/bin/bash # 1. for 循环遍历列表 echo for loop 1: list for i in 1 2 3 4 5; do echo Number: $i done echo for loop 2: sequence for i in {1..5}; do echo Number: $i done echo for loop 3: C-style for ((i0; i5; i)); do echo Index: $i done # 遍历当前目录下的 .txt 文件 echo for loop 4: files for file in *.txt; do if [ -f $file ]; then echo Processing file: $file # wc -l $file fi done # 2. while 循环条件为真时执行 echo while loop counter1 while [ $counter -le 5 ]; do echo Counter: $counter ((counter)) # 算术运算等同于 let countercounter1 done # 从文件或管道读取行 echo Reading from a here-string: while IFS read -r line; do echo Line: $line done This is line 1 This is line 2 # 3. until 循环条件为假时执行较少用 echo until loop counter1 until [ $counter -gt 5 ]; do echo Until Counter: $counter ((counter)) done # 循环控制break 和 continue echo break and continue for i in {1..10}; do if [ $i -eq 3 ]; then continue # 跳过本次循环剩余部分 fi if [ $i -eq 8 ]; then break # 跳出整个循环 fi echo i $i done网络热词关联shell脚本for循环是搜索高频词上述示例覆盖了其所有主要用法。while循环也常用于监控任务直到某个条件满足如进程结束、文件出现。5.6 函数封装可重用代码函数将一段代码逻辑封装起来提高代码复用性和可读性。#!/bin/bash # 函数定义 function greet() { local name$1 # 使用 local 声明局部变量避免污染全局 local hour$(date %H) if [ $hour -lt 12 ]; then echo Good morning, $name! elif [ $hour -lt 18 ]; then echo Good afternoon, $name! else echo Good evening, $name! fi return 0 # 显式返回状态码0表示成功 } # 另一种定义方式更简洁 log_message() { local level$1 local msg$2 echo [$(date %Y-%m-%d %H:%M:%S)] [$level] $msg } # 调用函数 greet Alice greet Bob log_message INFO Script started. log_message ERROR Something went wrong! 2 # 将错误信息输出到标准错误 # 函数返回值通过 $? 获取或者使用命令替换捕获输出 result$(greet Charlie) echo Function output: $result # 函数返回状态码检查 if ! greet David; then echo Greet function failed. 2 fi关键点函数内变量默认是全局的使用local关键字定义局部变量是良好习惯。函数“返回”值通常指两种1) 打印到标准输出的内容可用$(function)捕获2) 退出状态码return或最后一条命令的退出码通过$?获取。6. 文本处理“三剑客”grep, sed, awk这是 Shell 脚本处理文本数据的核心工具功能强大组合使用能解决绝大多数文本分析问题。6.1 grep全局正则表达式打印用于搜索过滤#!/bin/bash # 假设我们有一个日志文件 app.log cat app.log EOF 2023-10-27 10:00:01 INFO User login successful, user_id1001 2023-10-27 10:00:05 ERROR Database connection failed 2023-10-27 10:00:10 WARN High memory usage detected 2023-10-27 10:00:15 INFO User logout, user_id1001 2023-10-27 10:00:20 ERROR Payment service timeout EOF echo 1. 基本搜索查找包含 ERROR 的行 grep ERROR app.log echo -e \n 2. 忽略大小写查找 error 或 ERROR grep -i error app.log echo -e \n 3. 显示行号 grep -n INFO app.log echo -e \n 4. 反向搜索查找不包含 INFO 的行 grep -v INFO app.log echo -e \n 5. 正则表达式查找 user_id 为 1001 的日志 grep -E user_id1001 app.log # -E 启用扩展正则表达式 # 等价于 egrep user_id1001 app.log echo -e \n 6. 统计匹配行数 grep -c ERROR app.log echo -e \n 7. 递归搜索目录中的文件 # grep -r function_name /path/to/src/code/6.2 sed流编辑器用于文本替换、删除、插入#!/bin/bash # 示例文本 textThe quick brown fox jumps over the lazy dog. The dog is lazy. echo 原始文本: $text echo -e \n 1. 替换第一次出现的 dog 为 cat echo $text | sed s/dog/cat/ echo -e \n 2. 替换所有出现的 dog 为 cat (全局替换) echo $text | sed s/dog/cat/g echo -e \n 3. 删除包含 lazy 的行 echo $text | sed /lazy/d echo -e \n 4. 在匹配行后追加文本 echo $text | sed /fox/a\--- This line is added after fox --- echo -e \n 5. 直接修改文件危险先备份 # sed -i.bak s/old/new/g file.txt # -i 直接修改.bak 创建备份 # sed -i s/old/new/g file.txt # 直接修改无备份 echo -e \n 6. 使用不同分隔符当替换内容包含 / 时 path/usr/local/bin/app echo 原始路径: $path echo $path | sed s|/usr/local|/opt| # 使用 | 作为分隔符6.3 awk强大的文本分析工具支持编程awk将文本行视为由分隔符默认空格分隔的字段记录可以对这些字段进行复杂处理。#!/bin/bash # 示例数据模拟系统用户列表 cat users.txt EOF alice:x:1001:1001:Alice Smith:/home/alice:/bin/bash bob:x:1002:1002:Bob Johnson:/home/bob:/bin/zsh charlie:x:1003:1003:Charlie Brown:/home/charlie:/usr/sbin/nologin david:x:1004:1004:David Lee:/home/david:/bin/bash EOF echo 1. 打印指定列例如用户名和家目录 awk -F: {print User:, $1, Home:, $6} users.txt # -F: 指定冒号为字段分隔符 echo -e \n 2. 条件过滤打印使用 bash 的用户 awk -F: $7 /bin/bash {print $1} users.txt echo -e \n 3. 内置变量NR行号NF字段数 awk -F: {print NR, NF, $0} users.txt # 打印行号、字段数和整行 echo -e \n 4. 计算统计行数、计算UID总和 awk -F: BEGIN {count0; sum0} {count; sum$3} END {print Total users:, count, Sum of UIDs:, sum} users.txt echo -e \n 5. 字段匹配打印用户名包含 li 的行 awk -F: $1 ~ /li/ {print $0} users.txt # ~ 表示匹配正则表达式 echo -e \n 6. 修改输出格式printf awk -F: {printf %-10s (UID: %4d) - %s\n, $1, $3, $6} users.txt # 一个实用的组合例子分析 Nginx 访问日志统计每个IP的访问次数 # awk {print $1} access.log | sort | uniq -c | sort -nr | head -10“三剑客”组合使用示例从日志中提取特定时间段的错误信息并统计数量。# 假设日志格式包含时间戳 [2023-10-27 10:00:05] grep ERROR app.log | sed -n /2023-10-27 10:00:00/,/2023-10-27 10:01:00/p | awk {print $4} | sort | uniq -c | sort -nr7. 实战案例一个简单的日志分析监控脚本让我们将前面所学的知识整合起来编写一个实用的脚本。这个脚本会检查指定日志文件是否存在。统计过去5分钟内“ERROR”级别日志的出现次数。如果错误次数超过阈值则发送告警这里用打印消息模拟。将本次检查结果记录到另一个日志文件。#!/bin/bash # 文件名: log_monitor.sh # 功能监控应用日志中的错误数量 set -euo pipefail # 启用严格模式 # 配置区 LOG_FILE/var/log/myapp/app.log # 要监控的日志文件 ALERT_THRESHOLD5 # 错误次数告警阈值 CHECK_MINUTES5 # 检查过去几分钟的日志 REPORT_LOG./monitor_report.log # 脚本自身的运行日志 # 函数定义 # 记录脚本运行日志 log() { echo [$(date %Y-%m-%d %H:%M:%S)] $* | tee -a $REPORT_LOG } # 发送告警模拟 send_alert() { local message$1 log ALERT: $message # 实际环境中这里可以替换为 # 1. 发送邮件: mail -s App Error Alert adminexample.com $message # 2. 调用Webhook: curl -X POST -H Content-type: application/json --data {\text\:\$message\} $WEBHOOK_URL # 3. 发送短信或钉钉/企业微信消息 } # 主逻辑 log Starting log monitor # 1. 检查日志文件是否存在 if [[ ! -f $LOG_FILE ]]; then log ERROR: Log file $LOG_FILE does not exist. exit 1 fi # 2. 计算检查的时间点5分钟前 # 使用 date 命令计算时间兼容不同系统 if date --version /dev/null; then # GNU date (Linux) start_time$(date -d -${CHECK_MINUTES} minutes %Y-%m-%d %H:%M:%S) else # BSD date (macOS) start_time$(date -v -${CHECK_MINUTES}M %Y-%m-%d %H:%M:%S) fi log Checking logs since: $start_time # 3. 使用 grep 和 awk 统计错误数量 # 假设日志格式为: [2023-10-27 10:00:05] ERROR Some error message error_count$(grep ERROR $LOG_FILE | awk -v start$start_time { # 提取日志时间戳假设格式固定 log_time substr($0, 2, 19) # 提取 [2023-10-27 10:00:05] if (log_time start) { count } } END {print count0}) # 确保没有匹配时输出0 log Found $error_count ERROR(s) in the last ${CHECK_MINUTES} minute(s). # 4. 判断是否告警 if [[ $error_count -ge $ALERT_THRESHOLD ]]; then send_alert High error rate detected! Count: $error_count (Threshold: $ALERT_THRESHOLD). Please check $LOG_FILE immediately. else log Error count is within normal range. fi log Log monitor finished 如何使用这个脚本将脚本保存为log_monitor.sh。修改脚本开头的配置项指向真实的日志文件路径。赋予执行权限chmod x log_monitor.sh。手动测试./log_monitor.sh。添加到 crontab 定时任务例如每5分钟执行一次# 编辑当前用户的crontab crontab -e # 添加一行 */5 * * * * /full/path/to/log_monitor.sh /dev/null 218. 常见问题与排查方法在编写和运行 Shell 脚本时你一定会遇到各种问题。下表汇总了典型问题及其解决方法。问题现象可能原因排查方式解决方案执行脚本报错Permission denied脚本文件没有执行权限。ls -l script.sh查看权限。运行chmod x script.sh。执行脚本报错command not found1. 命令拼写错误。2. 命令未安装。3. 脚本第一行shebang路径错误。1. 检查拼写。2. 用which command检查命令是否存在。3. 检查#!/bin/bash路径用which bash确认。1. 纠正拼写。2. 安装对应软件包。3. 修正shebang行。变量值为空或未定义1. 变量名拼写错误。2. 变量作用域问题在子Shell中定义。3. 使用了set -u且变量未定义。1. 使用echo var$var调试。2. 检查是否在管道或$(...)子Shell中赋值。3. 检查脚本开头是否有set -u。1. 纠正变量名。2. 避免在子Shell中赋值需要后续使用的变量或使用进程替换、临时文件传递。3. 使用${var:-default}提供默认值。脚本在终端运行正常在 crontab 中不执行1. crontab 环境变量与终端不同如PATH。2. 使用了相对路径。3. 命令需要交互式终端。1. 在脚本中显式设置PATH等环境变量。2. 查看/var/log/cron或用户邮件获取错误信息。1. 在脚本开头设置PATH/usr/bin:/bin等。2. 使用绝对路径。3. 对于需要终端的命令考虑使用nohup或screen。grep或sed处理包含空格或特殊字符的文件名时出错未用引号包裹变量导致 Shell 进行单词分割和通配符扩展。检查脚本中引用文件变量的地方。始终用双引号包裹变量grep pattern $filename。rm -rf误删文件变量为空或路径错误导致rm -rf /或rm -rf *等灾难。1. 在rm -rf前echo要删除的路径。2. 使用set -u防止使用未定义变量。1. 使用rm -rf -- $dir(--表示选项结束)。2. 实现“干运行”模式if [[ $DRY_RUN ]]; then echo Would delete: $dir; else rm -rf $dir; fi。脚本执行到一半莫名其妙停止某条命令执行失败而脚本未做错误处理。在关键命令后检查$?或启用set -e。在脚本开头加set -e或对可能失败的命令进行判断if ! command; then echo Failed; exit 1; fi。网络热词问题输入adb shell后显示error: device not found这不是 Shell 脚本问题而是 Android Debug Bridge (ADB) 环境问题。设备未连接或未授权。1. 执行adb devices查看设备列表。2. 检查手机 USB 调试是否开启。3. 检查 USB 线连接和驱动。1. 在手机上确认“允许USB调试”的弹窗。2. 重启 adb 服务adb kill-server adb start-server。3. 换USB口或数据线。9. 最佳实践与编写建议遵循以下建议可以让你写出更健壮、可维护、安全的 Shell 脚本。脚本开头总是包含shebang(#!/bin/bash)。添加脚本用途、作者、版本、修改历史的注释。考虑启用严格模式set -euo pipefail。变量使用变量名使用小写常量或环境变量使用大写。对变量赋值和引用时始终使用双引号namevalueecho $name。在函数内部使用local声明变量。命令执行检查重要命令的返回值if command; then ...或command || { echo Failed; exit 1; }。使用$(command)而不是反引号command前者更清晰且支持嵌套。对于复杂的命令将其分解为多个步骤或封装进函数。输入验证永远不要相信外部输入。对脚本参数、用户输入、文件内容进行验证。使用[[ ]]进行条件测试它比[ ]更安全功能更强。路径处理在脚本开头定义基础路径使用绝对路径或相对于脚本位置的路径。使用dirname $0获取脚本所在目录。SCRIPT_DIR$(cd $(dirname $0) pwd) LOG_DIR${SCRIPT_DIR}/logs日志与调试为脚本添加日志功能记录其操作和错误。在开发阶段使用set -x进行调试。可以通过一个全局变量控制日志级别INFO, DEBUG, ERROR。代码风格保持代码缩进一致通常2或4个空格。添加清晰的注释解释复杂的逻辑或非显而易见的操作。将长脚本按功能拆分成多个函数甚至多个文件。安全须知避免在脚本中硬编码密码或密钥。使用环境变量或配置文件并确保文件权限安全。使用sudo时需格外小心尽量限定最小权限。对于从网络或用户获取的数据在执行前要进行转义防止命令注入。Shell 脚本的魅力在于其直接和高效它将系统命令和工具无缝连接是自动化任务的利器。从简单的文件备份到复杂的部署流水线掌握 Shell 脚本能让你从重复劳动中解放出来。学习的捷径就是多读、多写、多调试。建议从模仿本文的实战案例开始然后尝试解决自己工作中遇到的实际问题例如自动清理日志、批量转换图片格式、监控服务状态等。每次解决问题你对脚本的理解就会加深一层。
返回列表