兰州哪家网站做推广效果好,网站开发可以当程序员,小程序源码网站论坛,网站空间上传教程一、Shell 概述
1. 什么是 Shell#xff1f;
Shell 是 Linux 系统中用户与内核之间的桥梁#xff0c;作为 命令解析器#xff0c;它负责将用户输入的文本命令转换为计算机可执行的机器指令。
本质#xff1a;Shell 是一个程序#xff08;如常见的 Bash、Zsh#xff09…一、Shell 概述
1. 什么是 Shell
Shell 是 Linux 系统中用户与内核之间的桥梁作为 命令解析器它负责将用户输入的文本命令转换为计算机可执行的机器指令。
本质Shell 是一个程序如常见的 Bash、Zsh而 Shell 脚本则是包含一系列命令的文本文件通常以 .sh 结尾非强制仅为识别方便。作用批量执行重复命令、实现自动化任务、编写复杂逻辑程序。
2. 为什么选择 Bash
主流性BashBourne Again Shell是 Linux 系统的默认 Shell几乎所有发行版都内置兼容性强。功能强大支持变量、数组、函数、流程控制等高级特性满足从简单脚本到复杂程序的需求。学习门槛低语法简洁对新手友好且与早期 Shell如 Bourne Shell兼容。
二、Shell 脚本基础操作
1. 脚本文件的创建与编辑
1.1 选择文本编辑器
Shell 脚本本质是纯文本文件推荐使用以下编辑器
nano新手友好命令行下的简单编辑器通过 nano 文件名 启动支持鼠标和快捷键操作。vim功能强大通过 vim 文件名 启动需切换模式i 进入插入模式Esc 退出:wq 保存并退出。gedit图形界面适合桌面环境直接右键文件选择「用文本编辑器打开」。
1.2 创建脚本文件
# 在当前目录创建名为 demo.sh 的脚本文件
touch demo.sh1.3 编写脚本内容
# 用 nano 打开并编辑脚本
nano demo.sh输入以下内容
#!/bin/bash
# 这是一个简单的 Shell 脚本示例
echo Hello, World! # 输出文本关键行解释
#!/bin/bash指定脚本使用 Bash 解释器执行必须位于文件第一行。# 开头的行是注释用于解释代码逻辑不参与执行。echo 命令用于输出文本默认换行。若需不换行使用 echo -n 内容。
2. 脚本的执行方式
2.1 方式一通过 bash 命令执行无需权限
bash demo.sh原理直接调用 Bash 解释器执行脚本适用于快速测试无需修改文件权限。
2.2 方式二通过 sh 命令执行兼容旧版
sh demo.sh注意sh 通常指向 Bash但某些系统中可能指向更古老的 Shell如 BusyBox sh可能导致兼容性问题。建议统一使用 #!/bin/bash 头部。
2.3 方式三赋予执行权限后运行推荐
# 赋予文件执行权限
chmod x demo.sh
# 通过相对路径执行
./demo.sh关键点 chmod x 用于添加执行权限否则会提示 Permission denied。./ 表示当前目录必须显式指定路径因为当前目录默认不在系统 PATH 中。
3. 脚本执行的常见问题与解决
3.1 权限不足
错误提示Permission denied 解决方法
chmod x demo.sh # 赋予执行权限3.2 路径错误
错误提示No such file or directory 可能原因
脚本路径错误如 ./demo.sh 写成 demo.sh。脚本文件格式问题如 Windows 换行符导致的错误。 解决方法
# 检查路径是否正确
ls -l demo.sh # 确认文件存在且路径正确# 转换文件格式为 Unix 格式若文件来自 Windows
dos2unix demo.sh # 需先安装 dos2unix 工具3.3 解释器路径错误
错误提示Bad interpreter: No such file or directory 可能原因脚本头部 #!/bin/bash 路径错误。 解决方法
# 查看系统 Bash 路径
which bash # 通常输出 /bin/bash# 修改脚本头部为正确路径
vim demo.sh # 将第一行改为 #!/usr/bin/env bash更具可移植性4. 脚本调试与验证
4.1 检查执行结果
# 执行脚本并查看输出
./demo.sh # 正常输出Hello, World!# 检查命令执行状态0 表示成功
echo $? # 输出04.2 调试模式
# 启用调试模式显示每行执行的命令
bash -x demo.sh4.3 错误处理
# 脚本遇到错误时立即退出
set -e# 捕获错误并输出信息
trap echo 错误发生在第 $LINENO 行 ERR5. 脚本优化与进阶
5.1 输出重定向
# 将输出保存到文件覆盖原有内容
./demo.sh output.log# 追加输出到文件
./demo.sh output.log5.2 输入重定向
# 从文件读取输入
cat input.txt | ./demo.sh5.3 环境变量与脚本交互
# 在脚本中引用环境变量
echo 当前用户$USER主目录$HOME# 导出自定义变量到子进程
export MY_VAR自定义变量6. 实战案例批量创建用户
#!/bin/bash
# 批量创建用户脚本# 定义用户列表
users(user1 user2 user3)# 遍历用户列表并创建用户
for user in ${users[]}; douseradd $user echo 用户 $user 创建成功 || echo 用户 $user 创建失败
done执行步骤 保存脚本为 create_users.sh。赋予执行权限chmod x create_users.sh。执行脚本./create_users.sh。
7. 常见易错点总结
变量赋值空格a 10 错误必须为 a10。中括号空格[条件] 需写成 [ 条件 ]如 [ $a -gt 5 ]。路径问题执行脚本需用 ./脚本名直接输入 脚本名 会提示 “命令未找到”。转义符遗漏使用 expr 计算乘法时* 需转义为 \*如 expr 5 \* 3。文件格式错误Windows 格式文件需转换为 Unix 格式使用 dos2unix。
8. 拓展知识
8.1 脚本可移植性
推荐头部#!/usr/bin/env bash使用 env 命令自动查找 Bash 路径避免硬编码。兼容性检查使用 sh 命令测试脚本在旧版 Shell 中的运行情况。
8.2 权限设置最佳实践
最小权限原则仅赋予脚本所有者执行权限chmod ux 脚本名。特殊权限setuidchmod us 脚本名允许普通用户以脚本所有者权限执行。
8.3 脚本性能优化
使用内置命令优先使用 Bash 内置命令如 echo、cd避免调用外部程序。减少 I/O 操作将多次 echo 合并为一次输出或使用 printf 提升效率。 通过以上步骤你可以全面掌握 Shell 脚本的基础操作从创建、编辑到执行、调试再到优化和实战应用。建议结合实际案例反复练习加深对脚本执行原理的理解。
三、变量脚本的 “数据细胞”
1. 变量基础从存储到操作
1.1 变量定义与引用
定义变量
nameAlice # 字符串变量值含空格需用引号包裹
age25 # 数值变量本质为字符串可参与计算
file_path/etc/passwd # 路径变量关键规则 变量名必须以字母或下划线开头区分大小写如 Name 和 name 是不同变量。等号两边不能有空格name Alice 会报错。 引用变量
echo 姓名$name年龄${age}岁
# 输出姓名Alice年龄25岁推荐写法使用 ${变量名} 避免歧义例如
fruitapple
echo ${fruit}s # 输出apples正确
echo $fruits # 输出错误变量名歧义1.2 单引号 vs 双引号
符号特性示例原样输出不解析变量和转义符echo $name → $name解析变量和转义符如 \n 换行echo $name\n → Alice 换行 实战场景
msg当前用户$USER主目录$HOME
echo $msg # 输出当前用户$USER主目录$HOMEmsg当前用户$USER主目录$HOME
echo $msg # 输出当前用户root主目录/root2. 数值计算从基础到高级
2.1 算术运算符
运算符示例a10, b3结果$((a b))13-$((a - b))7*$((a * b))30/$((a / b))3%$((a % b))1
推荐方法
# 方法 1$(( ))简洁高效
sum$((5 3)) # 8
product$((5 * 3)) # 15# 方法 2expr需转义乘号
sum$(expr 5 3) # 8
product$(expr 5 \* 3) # 15注意 \*2.2 数值运算实战
案例计算圆的面积
#!/bin/bash
radius5
area$((3.14 * radius * radius)) # 注意整数运算会截断小数
echo 半径为 $radius 的圆面积$area # 输出78实际应为 78.5改进方案
area$(echo 3.14 * $radius * $radius | bc) # 使用 bc 工具支持浮点运算
echo 半径为 $radius 的圆面积$area # 输出78.53. 标准变量系统级的数据仓库
3.1 常用环境变量
变量名含义示例管理员用户$HOME用户主目录/root$PWD当前工作目录/home/user/scripts$USER当前用户名root$PATH命令搜索路径:/usr/bin:/bin$HOSTNAME主机名localhost.localdomain 实战示例
echo 当前用户$USER主目录$HOME
# 输出当前用户root主目录/root3.2 永久设置环境变量
临时生效当前终端有效 export MY_VAR自定义变量永久生效所有终端有效 # 编辑用户配置文件
nano ~/.bashrc
# 在文件末尾添加
export MY_VAR自定义变量
# 使配置生效
source ~/.bashrc4. 特殊变量脚本参数与状态
4.1 位置参数
变量含义示例脚本 test.sh 1 2 a b$0脚本名称test.sh$1~$9第 1 到第 9 个参数$11, $22, $3a b${10}第 10 个参数需用大括号$1010若参数足够 示例脚本 args.sh #!/bin/bash
echo 脚本名$0
echo 第一个参数$1
echo 第十个参数${10}运行 ./args.sh 1 2 3 4 5 6 7 8 9 10
# 输出
# 脚本名./args.sh
# 第一个参数1
# 第十个参数104.2 其他特殊变量
变量含义示例脚本 test.sh$#参数个数3若传递 3 个参数$所有参数独立字符串1 2 a b$*所有参数单个字符串1 2 a b空格丢失$$脚本进程号PID12345实际 PID$?上条命令退出状态0 成功0若命令成功 实战案例
#!/bin/bash
echo 参数个数$#
echo 所有参数\$$
echo 所有参数\$*$*运行
./test.sh hello world!
# 输出
# 参数个数2
# 所有参数$hello world!
# 所有参数$*hello world!5. 变量作用域从全局到局部
5.1 全局变量
定义在脚本任何位置定义的变量默认在整个脚本有效。 #!/bin/bash
global_var全局变量function show_var() {echo 函数内访问全局变量$global_var
}show_var # 输出函数内访问全局变量全局变量
echo 函数外访问全局变量$global_var # 输出函数外访问全局变量全局变量5.2 局部变量
定义使用 local 关键字在函数内定义的变量仅在函数内有效。
#!/bin/bash
function local_var_demo() {local local_var局部变量 # 仅函数内有效echo 函数内访问局部变量$local_var
}local_var_demo # 输出函数内访问局部变量局部变量
echo 函数外访问局部变量$local_var # 输出函数外访问局部变量空6. 高级变量操作让脚本更灵活
6.1 变量替换
语法作用示例strhello world${str#h*o}从头部删除最短匹配 h*oworld${str##h*o}从头部删除最长匹配 h*orld${str%ld}从尾部删除最短匹配 ldhello wor${str%%ld}从尾部删除最长匹配 ldhello wor${str/world/Shell}替换第一个匹配项hello Shell${str//l/LL}替换所有匹配项heLLo worLLd 实战案例
path/home/user/documents/report.txt
# 提取文件名
filename${path##*/} # 输出report.txt
# 提取文件类型
extension${filename##*.} # 输出txt6.2 命令替换
语法
变量$(命令) # 推荐写法
变量命令 # 反引号写法易混淆示例
# 获取当前日期
date$(date %Y-%m-%d)
echo 今天日期$date # 输出今天日期2023-10-01# 获取文件行数
line_count$(wc -l /etc/passwd)
echo 用户文件行数$line_count # 输出用户文件行数427. 常见易错点与解决方案
7.1 变量赋值空格错误
错误示例
name Alice # 报错-bash: name: 未找到命令解决方案
nameAlice # 正确写法7.2 中括号条件判断空格缺失
错误示例
if [ $age -gt 18 ]; then # 正确
if [ $age-gt 18 ]; then # 错误缺少空格7.3 数组定义逗号分隔
错误示例
names(Kanye, Edison, Fish) # 错误逗号分隔解决方案
names(Kanye Edison Fish) # 正确空格分隔7.4 变量命名冲突
错误示例
USER自定义用户 # 覆盖系统变量 $USER解决方案
user自定义用户 # 使用小写字母避免冲突8. 拓展知识让变量更强大
8.1 只读变量
readonly PI3.14 # 定义只读变量
PI3.1415 # 报错PI: 只读变量8.2 删除变量
nameAlice
unset name # 删除变量
echo $name # 输出空8.3 变量类型转换
num123
echo $((num 100)) # 输出223自动转换为整数9. 实战案例变量综合应用
9.1 批量重命名文件
#!/bin/bash
# 将当前目录下所有 .txt 文件重命名为 .log
for file in *.txt; donew_name${file%.txt}.log # 替换扩展名mv $file $new_nameecho 重命名$file → $new_name
done9.2 动态获取系统信息
#!/bin/bash
# 获取系统负载、内存使用、用户数
load$(uptime | awk -F load average: {print $2} | cut -d , -f 1)
mem_used$(free -h | awk /Mem:/ {print $3})
user_count$(who | wc -l)echo 系统负载$load
echo 内存使用$mem_used
echo 在线用户$user_count10. 总结变量的 “生存法则”
命名规范小写字母开头避免与系统变量冲突。引号使用值含空格或特殊字符时优先使用双引号。作用域控制函数内变量使用 local 声明避免全局污染。性能优化算术运算用 $(( ))命令替换用 $( )。 通过以上内容你将掌握 Shell 变量的核心操作从基础定义到高级应用再到实战案例逐步提升脚本编写能力。变量是 Shell 编程的基石熟练运用它们能让你的脚本更灵活、高效
四、运算符与条件判断
1. 关系运算符判断条件
1数字比较
运算符含义示例a10b20-eq等于[ $a -eq $b ] → 假-ne不等于[ $a -ne $b ] → 真-gt大于[ $a -gt $b ] → 假-lt小于[ $a -lt $b ] → 真-ge大于等于[ $a -ge $b ] → 假-le小于等于[ $a -le $b ] → 真
2字符串比较
运算符含义示例-z字符串为空[ -z ] → 真-n字符串非空[ -n abc ] → 真字符串相等[ a a ] → 真!字符串不等[ a ! b ] → 真\字符串排序大于需转义[ b \ a ] → 真\字符串排序小于需转义[ a \ b ] → 真
3文件判断
运算符含义示例-e文件 / 目录存在[ -e /etc/passwd ] → 真-f是普通文件[ -f first_script.sh ] → 真若文件存在-d是目录[ -d /home ] → 真-r文件可读[ -r /etc/shadow ] → 假普通用户不可读-w文件可写[ -w first_script.sh ] → 真若有写权限-x文件可执行[ -x first_script.sh ] → 真若有执行权限
2. 逻辑运算符组合条件
运算符含义示例-a逻辑与AND[ $a -gt 5 -a $a -lt 15 ] → a 在 6-14 之间为真-o逻辑或OR[ -f file -o -d dir ] → 文件或目录存在为真!逻辑非NOT[ ! -e file ] → 文件不存在为真 注意 条件判断需用中括号 [ ]且括号前后必须留空格如 [ $a -gt 5 ]否则报错。复杂条件可用 和 ||适用于命令级逻辑如 command1 command2 表示 command1 成功后执行 command2。
五、数组批量数据处理
1. 定义数组
方式 1直接赋值下标从 0 开始 fruits(apple banana orange) # 定义包含三个元素的数组方式 2指定下标支持稀疏数组 numbers[0]10
numbers[2]30 # 下标 1 未定义值为空方式 3省略下标自动递增 array()
array(one) # 追加元素
array(two)2. 访问数组元素
获取单个元素${数组名[下标]} echo ${fruits[1]} # 输出banana获取所有元素${数组名[]} 或 ${数组名[*]} echo ${fruits[]} # 输出apple banana orange获取数组长度${#数组名[]} echo ${#fruits[]} # 输出3切片操作从下标 1 开始取 2 个元素 echo ${fruits[]:1:2} # 输出banana orange3. 遍历数组示例
#!/bin/bash
nums(1 3 5 7 9)
for num in ${nums[]}; do # 遍历数组所有元素echo 当前数字$num
done
# 输出
# 当前数字1
# 当前数字3
# 当前数字5
# 当前数字7
# 当前数字9六、流程控制脚本的 “逻辑大脑”
在 Shell 编程中流程控制是实现复杂逻辑的核心。通过条件判断和循环结构脚本可以根据不同场景执行不同操作实现自动化任务。本节将从基础语法到实战案例逐步解析 Shell 流程控制的核心知识点。
1. 条件判断让脚本 “会思考”
1.1 if 语句最基础的条件分支
语法格式
if [ 条件 ]; then命令1 # 条件为真时执行
elif [ 条件2 ]; then # 可选多个条件分支命令2
else # 可选所有条件不满足时执行命令3
fi # 必须以 fi 结束关键细节 条件表达式需用中括号 [ ] 包裹且括号前后必须留空格如 [ $a -gt 5 ]否则报错。文件判断参数常用 -e存在、-f普通文件、-d目录等见下表。
运算符含义示例-e文件 / 目录存在[ -e /etc/passwd ] → 真-f是普通文件[ -f script.sh ] → 真若文件存在-d是目录[ -d /home ] → 真 示例判断文件类型
#!/bin/bash
file./test.txtif [ -e $file ]; then # 文件存在if [ -f $file ]; then # 是普通文件echo 文件 $file 是普通文件elif [ -d $file ]; then # 是目录echo 文件 $file 是目录else # 其他类型如链接、设备文件echo 文件 $file 是特殊文件fi
elseecho 文件 $file 不存在
fi1.2 case 语句模式匹配的高效选择
语法格式
case 变量 in模式1)命令1;; # 必须用双分号结束分支模式2)命令2;;*) # 通配符匹配所有未定义的模式命令3;;
esac # 必须以 esac 结束适用场景 菜单驱动程序如用户输入 1-5 选择操作。文件类型判断如根据扩展名执行不同解压命令。 示例简易菜单程序
#!/bin/bash
echo 请选择操作1-3
echo 1. 查看当前目录
echo 2. 查看系统时间
echo 3. 退出程序
read choicecase $choice in1)ls -l # 列出当前目录文件;;2)date %Y-%m-%d %H:%M:%S # 显示当前时间;;3)echo 退出程序exit 0 # 退出脚本;;*)echo 无效选择请输入 1-3;;
esac2. 循环结构让脚本 “重复执行”
2.1 for 循环遍历列表或范围
格式 1遍历列表新手友好
for 变量 in 元素1 元素2 元素3; do命令 # 对每个元素执行操作
done示例打印所有水果
fruits(apple banana orange)
for fruit in ${fruits[]}; doecho 当前水果$fruit
done
# 输出
# 当前水果apple
# 当前水果banana
# 当前水果orange格式 2C 语言风格指定次数
for ((初始值; 条件; 增量)); do命令 # 按次数循环
done示例计算 12…10
sum0
for ((i1; i10; i)); dosum$((sum i))
done
echo 总和$sum # 输出552.2 while 循环条件驱动的重复
语法格式
while [ 条件 ]; do命令 # 条件为真时持续执行
done示例逐行读取文件
#!/bin/bash
fileusers.txt
while read line; do # 每次读取文件一行到变量 lineecho 用户$line
done $file # 从文件获取输入重定向2.3 until 循环反条件循环
语法格式
until [ 条件 ]; do命令 # 条件为假时持续执行直到条件为真
done示例等待文件生成
until [ -e data.csv ]; do # 直到文件存在echo 等待 data.csv 生成...sleep 1 # 休眠 1 秒
done
echo 文件已生成3. 循环控制让流程更灵活
3.1 break 与 continue
关键字作用示例break跳出当前循环类似 C 语言for i in 1 2 3; do if [ $i -eq 2 ]; then break; fi; done仅打印 1continue跳过当前循环迭代for i in 1 2 3; do if [ $i -eq 2 ]; then continue; fi; echo $i; done打印 1, 3
3.2 嵌套循环解决复杂逻辑
示例打印乘法表
for i in {1..9}; dofor j in {1..9}; doecho -n $i×$j$((i*j)) # -n 不换行doneecho # 换行
done4. 函数代码复用的 “积木”
4.1 定义与调用函数
语法格式
# 格式 1简洁写法
函数名() {local 变量 # 声明局部变量仅限函数内使用命令 # 函数逻辑return 退出码 # 可选0 表示成功非 0 表示失败
}# 格式 2显式声明
function 函数名() {命令
}示例计算两数之和
#!/bin/bash
# 定义函数接收两个参数返回和
add() {local a$1 # 局部变量避免污染全局作用域local b$2echo $((a b)) # 通过 echo 输出结果推荐return 0 # 返回成功状态
}# 调用函数并获取结果
result$(add 5 3)
echo 5 3 $result # 输出8
echo 函数返回值$? # 输出0成功4.2 函数参数传递
位置参数函数内通过 $1、$2 等获取调用时传递的参数。参数验证调用前检查参数个数避免空指针错误。 add() {if [ $# -ne 2 ]; then # 检查参数个数是否为 2echo 错误需要 2 个参数return 1fi# 逻辑代码
}5. 常见易错点与解决方案
5.1 中括号空格缺失
错误示例
if [ $age18 ]; then # 错误缺少空格
if [ $age -gt 18 ]; then # 正确解决方案始终在中括号内外留空格[ 条件 ]。
5.2 case 分支遗漏 ;;
错误示例
case $choice in1) echo 选项 1 # 缺少 ;;导致语法错误
esac解决方案每个分支必须以 ;; 结束。
5.3 无限循环陷阱
错误示例
while [ 1 -eq 1 ]; do # 条件永远为真导致无限循环echo 陷阱
done解决方案确保循环条件最终会变为假或用 break 强制退出。
6. 实战案例流程控制综合应用
6.1 文件备份脚本
#!/bin/bash
# 功能判断目录是否存在存在则备份否则创建并备份backup_dir/backup
source_dir/data# 判断备份目录是否存在
if [ ! -d $backup_dir ]; thenmkdir -p $backup_dir # 创建目录-p 自动创建父目录echo 创建备份目录$backup_dir
fi# 备份数据使用时间戳命名备份文件
timestamp$(date %Y%m%d%H%M%S)
tar -czf $backup_dir/data_$timestamp.tar.gz $source_direcho 备份完成文件路径$backup_dir/data_$timestamp.tar.gz6.2 交互式猜数字游戏
#!/bin/bash
# 生成 1-100 随机数
num$((RANDOM % 100 1))
attempts0 # 记录尝试次数while true; do # 无限循环直到猜对read -p 请输入一个数字1-100 guessattempts$((attempts 1))if [ $guess -eq $num ]; thenecho 恭喜你在 $attempts 次内猜对了break # 跳出循环elif [ $guess -gt $num ]; thenecho 猜大了再试一次。elseecho 猜小了再试一次。fi
done7. 拓展知识让流程控制更强大
7.1 复合条件表达式
逻辑与如 command1 command2仅当 command1 成功时执行 command2。逻辑或||如 command1 || command2仅当 command1 失败时执行 command2。
7.2 函数递归
示例计算阶乘递归实现
factorial() {local n$1if [ $n -eq 0 ]; thenecho 1elseecho $((n * $(factorial $((n-1)))))fi
}result$(factorial 5)
echo 5 的阶乘$result # 输出1207.3 循环性能优化
减少 I/O将多次 echo 合并为一次或使用 printf 提升效率。避免全局变量函数内使用 local 声明变量提高代码可读性和安全性。
8. 总结流程控制的 “黄金法则”
条件判断善用 if 和 case复杂逻辑用 case 提高可读性。循环选择列表遍历用 for条件驱动用 while反向条件用 until。函数设计参数验证、局部变量、明确返回值提升代码复用性。调试技巧用 set -x 开启调试模式查看循环和条件的执行流程。
通过掌握流程控制你将能编写具备 “智能” 的 Shell 脚本实现从简单任务到复杂自动
七、函数代码复用的核心
在 Shell 编程中函数是实现代码复用和模块化的关键。通过将重复或通用的逻辑封装为函数不仅能减少代码冗余还能提高脚本的可读性和维护性。本节将从函数的基础语法出发结合实战案例逐步解析函数的核心知识点。
1. 函数基础从定义到调用
1.1 函数定义的两种格式
格式 1简洁写法推荐新手
函数名() {命令1命令2return 退出码 # 可选默认返回最后一条命令的状态0-255
}格式 2显式声明清晰易读
function 函数名() {命令
}关键说明
function 关键字可选但显式声明能提高代码可读性。return 用于指定退出码0 表示成功非 0 表示失败省略时返回最后一条命令的状态。
示例定义一个打招呼函数
greet() {echo Hello, $1! # $1 是函数的第一个参数return 10 # 手动设置返回码为 10
}1.2 调用函数与参数传递
语法
函数名 参数1 参数2 参数3 # 参数之间用空格分隔示例调用打招呼函数
greet Alice # 输出Hello, Alice!
echo 函数返回码$? # 输出10通过 $? 获取返回码2. 参数处理让函数更灵活
2.1 位置参数函数的 “输入变量”
变量含义示例函数调用 add 5 3$1第一个参数5$2第二个参数3$#参数个数2$所有参数独立字符串5 3 示例计算两数之和的函数
add() {local sum$(( $1 $2 )) # local 声明局部变量避免污染全局作用域echo 和为$sum # 输出结果推荐通过 echo 返回数据return 0 # 返回成功状态码
}# 调用函数并获取结果
result$(add 5 3) # 将函数输出赋值给变量
echo 计算结果$result # 输出计算结果82.2 参数验证避免无效输入
场景当函数需要固定数量的参数时先检查参数个数。
add() {if [ $# -ne 2 ]; then # 检查参数是否为 2 个echo 错误需要 2 个参数实际 $# 个return 1 # 返回错误码filocal sum$(( $1 $2 ))echo $sum
}# 调用错误示例
add 5 # 输出错误需要 2 个参数实际 1 个
echo 返回码$? # 输出13. 变量作用域避免 “变量污染”
3.1 全局变量脚本内处处可见
特点在函数外定义的变量或函数内未用 local 声明的变量均可在全局访问。
global_var全局变量show_global() {echo 函数内访问$global_var # 可直接访问全局变量
}show_global # 输出函数内访问全局变量
echo 函数外访问$global_var # 输出函数外访问全局变量3.2 局部变量函数内的 “私有数据”
语法用 local 关键字声明仅在函数内有效。
function local_demo() {local local_var局部变量 # 局部变量echo 函数内$local_var
}local_demo # 输出函数内局部变量
echo 函数外$local_var # 输出空外部无法访问4. 返回值状态与数据的双重传递
4.1 返回码状态值
用途通过 return 声明用于表示函数执行是否成功0 成功非 0 失败。获取方式调用后通过 $? 获取。
check_file() {if [ -e $1 ]; thenreturn 0 # 文件存在返回成功码elsereturn 1 # 文件不存在返回错误码fi
}check_file test.sh
if [ $? -eq 0 ]; thenecho 文件存在
elseecho 文件不存在
fi4.2 数据返回推荐方式
用途通过 echo 或 printf 输出数据适用于返回字符串、数值等复杂结果。获取方式用命令替换 $(函数名) 接收输出。
get_current_time() {date %Y-%m-%d %H:%M:%S # 直接输出时间
}time_now$(get_current_time)
echo 当前时间$time_now # 输出当前时间2023-10-01 15:30:005. 高级技巧让函数更强大
5.1 函数递归用循环逻辑解决复杂问题
场景计算阶乘、斐波那契数列等递归问题。
# 计算 n 的阶乘递归实现
factorial() {local n$1if [ $n -eq 0 ]; thenecho 1 # 递归终止条件elseecho $(( $n * $(factorial $((n-1))) )) # 递归调用fi
}result$(factorial 5)
echo 5 的阶乘$result # 输出1205.2 默认参数让函数更 “智能”
语法通过 ${参数:-默认值} 实现参数默认值。
greet() {local name${1:-Guest} # 若未传参默认值为 Guestecho Hello, $name!
}greet # 输出Hello, Guest!未传参时用默认值
greet Alice # 输出Hello, Alice!传参时用实际值5.3 可变参数处理不确定数量的输入
场景函数需要接收任意数量的参数如日志函数记录多个信息。
log() {local timestamp$(date %Y-%m-%d %H:%M:%S)echo [${timestamp}] $* # $* 表示所有参数视为单个字符串
}log 用户登录 IP: 192.168.1.1 # 输出[2023-10-01 15:30:00] 用户登录 IP: 192.168.1.16. 常见易错点与解决方案
6.1 忘记声明局部变量导致全局污染
错误示例
count0 # 全局变量
increment() {count$((count 1)) # 未用 local修改全局变量
}increment
echo 全局 count$count # 输出1全局变量被修改解决方案在函数内用 local 声明变量
increment() {local count$((count 1)) # 局部变量不影响全局
}6.2 参数索引错误如 $0 误用
错误示例
add() {echo $0 # 输出脚本名而非第一个参数$0 是脚本名参数从 $1 开始
}解决方案牢记函数内参数从 $1 开始$0 是脚本名。
6.3 返回码与数据返回混淆
错误做法用 return 返回数据仅支持 0-255 的整数。
add() {return $((5 3)) # 错误return 只能返回状态码
}正确做法用 echo 输出数据return 仅用于状态码。
7. 实战案例函数综合应用
7.1 文件操作函数库
需求封装常用文件操作函数如创建目录、复制文件。
#!/bin/bash# 函数 1创建目录带错误处理
create_dir() {local dir$1if [ -d $dir ]; thenecho 目录 $dir 已存在return 1fimkdir -p $dirif [ $? -eq 0 ]; thenecho 目录 $dir 创建成功return 0elseecho 目录 $dir 创建失败return 1fi
}# 函数 2复制文件到目录
copy_file() {local src$1local dest_dir$2if [ ! -f $src ]; thenecho 源文件 $src 不存在return 1fiif [ ! -d $dest_dir ]; thencreate_dir $dest_dir # 调用其他函数if [ $? -ne 0 ]; thenreturn 1fificp $src $dest_direcho 文件 $src 复制到 $dest_dir 成功return 0
}# 调用函数
copy_file data.txt backup7.2 交互式菜单函数
需求通过函数实现菜单驱动的用户交互。
show_menu() {echo 菜单 echo 1. 查看系统信息echo 2. 退出程序echo
}handle_choice() {local choice$1case $choice in1)uname -a # 显示系统信息;;2)echo 退出程序exit 0;;*)echo 无效选择;;esac
}# 主程序
while true; doshow_menu # 调用菜单函数read -p 请选择 choicehandle_choice $choice # 调用选择处理函数
done8. 拓展知识函数的进阶应用
8.1 函数库管理
创建函数文件将常用函数保存到独立文件如 utils.sh。 # utils.sh 内容
function add() { ... }
function greet() { ... }引入函数库通过 source 命令在脚本中引用。 source utils.sh # 使 utils.sh 中的函数在当前脚本生效
add 5 3 # 直接调用8.2 函数调试技巧
开启调试模式用 set -x 跟踪函数执行步骤。 set -x # 开启调试
add 5 3 # 显示每一步执行的命令
set x # 关闭调试打印参数信息在函数开头输出参数确认输入是否正确。 add() {echo 接收到的参数$1, $2 # 调试用输出...
}9. 总结函数的 “复用哲学”
代码复用将重复逻辑封装为函数避免 “重复造轮子”。模块化设计每个函数专注于一个独立功能如文件操作、数据计算提高可维护性。错误处理通过参数验证和返回码让函数更健壮。 掌握函数后你将从 “编写零散命令” 进阶到 “构建结构化脚本”。建议从简单函数开始逐步积累常用工具函
八、实战案例判断闰年
需求
输入年份判断是否为闰年闰年条件能被 4 整除且不能被 100 整除或能被 400 整除。
脚本实现
#!/bin/bash
read -p 请输入年份 year# 组合条件(year%4000) 或 (year%40 且 year%100!0)
if [ $((year % 400)) -eq 0 ] || [ \( $((year % 4)) -eq 0 -a $((year % 100)) -ne 0 \) ]; thenecho $year 是闰年
elseecho $year 是平年
fi关键点
\( 和 \) 用于转义括号确保条件正确组合。|| 表示逻辑或-a 表示逻辑与。
九、常见易错点总结
变量赋值空格a 10 错误必须为 a10等号前后不能有空格。中括号空格[条件] 需写成 [ 条件 ]否则报错如 [a -gt 5] 错误应为 [ $a -gt 5 ]。文件路径错误执行脚本时需用 ./脚本名直接输入 脚本名 会提示 “命令未找到”。转义符遗漏使用 expr 计算乘法时* 需转义为 \*或改用 $(( )) 避免转义。字符串比较误区比较字符串是否相等时 前后需留空格如 [ $a $b ]否则会被视为赋值。
十、总结
Shell 编程是 Linux 自动化的核心技能从简单的脚本到复杂的流程控制需要通过大量实践掌握。新手入门时建议 从单个知识点入手如变量、循环、函数逐个击破。每学一个语法编写小例子验证效果理解背后逻辑。遇到错误时善用 echo 打印变量值或用 bash -x 脚本名 调试显示每行执行过程。 记住Shell 脚本的魅力在于 “用简单命令组合实现强大功能”坚持练习你会逐渐体会到它的高效与便捷