linux入门六:Linux Shell 编程
一、Shell 概述
1. 什么是 Shell?
Shell 是 Linux 系统中用户与内核之间的桥梁,作为 命令解析器,它负责将用户输入的文本命令转换为计算机可执行的机器指令。
- 本质:Shell 是一个程序(如常见的 Bash、Zsh),而 Shell 脚本则是包含一系列命令的文本文件,通常以
.sh
结尾(非强制,仅为识别方便)。 - 作用:批量执行重复命令、实现自动化任务、编写复杂逻辑程序。
2. 为什么选择 Bash?
- 主流性:Bash(Bourne 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.sh
1.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 $? # 输出:0
4.2 调试模式
# 启用调试模式,显示每行执行的命令
bash -x demo.sh
4.3 错误处理
# 脚本遇到错误时立即退出
set -e# 捕获错误并输出信息
trap 'echo "错误发生在第 $LINENO 行"' ERR
5. 脚本优化与进阶
5.1 输出重定向
# 将输出保存到文件(覆盖原有内容)
./demo.sh > output.log# 追加输出到文件
./demo.sh >> output.log
5.2 输入重定向
# 从文件读取输入
cat input.txt | ./demo.sh
5.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
错误,必须为a=10
。 - 中括号空格:
[条件]
需写成[ 条件 ]
(如[ $a -gt 5 ]
)。 - 路径问题:执行脚本需用
./脚本名
,直接输入脚本名
会提示 “命令未找到”。 - 转义符遗漏:使用
expr
计算乘法时,*
需转义为\*
(如expr 5 \* 3
)。 - 文件格式错误:Windows 格式文件需转换为 Unix 格式(使用
dos2unix
)。
8. 拓展知识
8.1 脚本可移植性
- 推荐头部:
#!/usr/bin/env bash
,使用env
命令自动查找 Bash 路径,避免硬编码。 - 兼容性检查:使用
sh
命令测试脚本在旧版 Shell 中的运行情况。
8.2 权限设置最佳实践
- 最小权限原则:仅赋予脚本所有者执行权限(
chmod u+x 脚本名
)。 - 特殊权限:
setuid
(chmod u+s 脚本名
)允许普通用户以脚本所有者权限执行。
8.3 脚本性能优化
- 使用内置命令:优先使用 Bash 内置命令(如
echo
、cd
),避免调用外部程序。 - 减少 I/O 操作:将多次
echo
合并为一次输出,或使用printf
提升效率。
通过以上步骤,你可以全面掌握 Shell 脚本的基础操作,从创建、编辑到执行、调试,再到优化和实战应用。建议结合实际案例反复练习,加深对脚本执行原理的理解。
三、变量:脚本的 “数据细胞”
1. 变量基础:从存储到操作
1.1 变量定义与引用
定义变量:
name="Alice" # 字符串变量(值含空格需用引号包裹)
age=25 # 数值变量(本质为字符串,可参与计算)
file_path="/etc/passwd" # 路径变量
关键规则:
- 变量名必须以字母或下划线开头,区分大小写(如
Name
和name
是不同变量)。 - 等号两边不能有空格(
name = "Alice"
会报错)。
引用变量:
echo "姓名:$name,年龄:${age}岁"
# 输出:姓名:Alice,年龄:25岁
推荐写法:使用 ${变量名}
避免歧义,例如:
fruit="apple"
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,主目录:/root
2. 数值计算:从基础到高级
2.1 算术运算符
运算符 | 示例(a=10 , b=3 ) | 结果 |
---|---|---|
+ | $((a + b)) | 13 |
- | $((a - b)) | 7 |
* | $((a * b)) | 30 |
/ | $((a / b)) | 3 |
% | $((a % b)) | 1 |
推荐方法:
# 方法 1:$(( ))(简洁高效)
sum=$((5 + 3)) # 8
product=$((5 * 3)) # 15# 方法 2:expr(需转义乘号)
sum=$(expr 5 + 3) # 8
product=$(expr 5 \* 3) # 15(注意 `\*`)
2.2 数值运算实战
案例:计算圆的面积
#!/bin/bash
radius=5
area=$((3.14 * radius * radius)) # 注意:整数运算会截断小数
echo "半径为 $radius 的圆面积:$area" # 输出:78(实际应为 78.5)
改进方案:
area=$(echo "3.14 * $radius * $radius" | bc) # 使用 bc 工具支持浮点运算
echo "半径为 $radius 的圆面积:$area" # 输出:78.5
3. 标准变量:系统级的数据仓库
3.1 常用环境变量
变量名 | 含义 | 示例(管理员用户) |
---|---|---|
$HOME | 用户主目录 | /root |
$PWD | 当前工作目录 | /home/user/scripts |
$USER | 当前用户名 | root |
$PATH | 命令搜索路径 | :/usr/bin:/bin |
$HOSTNAME | 主机名 | localhost.localdomain |
实战示例:
echo "当前用户:$USER,主目录:$HOME"
# 输出:当前用户:root,主目录:/root
3.2 永久设置环境变量
- 临时生效(当前终端有效):
export MY_VAR="自定义变量"
- 永久生效(所有终端有效):
# 编辑用户配置文件 nano ~/.bashrc # 在文件末尾添加 export MY_VAR="自定义变量" # 使配置生效 source ~/.bashrc
4. 特殊变量:脚本参数与状态
4.1 位置参数
变量 | 含义 | 示例(脚本 test.sh 1 2 "a b" ) |
---|---|---|
$0 | 脚本名称 | test.sh |
$1~$9 | 第 1 到第 9 个参数 | $1=1 , $2=2 , $3=a b |
${10} | 第 10 个参数(需用大括号) | $10=10 (若参数足够) |
示例脚本 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
# 第十个参数:10
4.2 其他特殊变量
变量 | 含义 | 示例(脚本 test.sh ) |
---|---|---|
$# | 参数个数 | 3 (若传递 3 个参数) |
$@ | 所有参数(独立字符串) | 1 2 "a b" |
$* | 所有参数(单个字符串) | 1 2 a b (空格丢失) |
$$ | 脚本进程号(PID) | 12345 (实际 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 变量替换
语法 | 作用 | 示例(str="hello world" ) |
---|---|---|
${str#h*o} | 从头部删除最短匹配 h*o | world |
${str##h*o} | 从头部删除最长匹配 h*o | rld |
${str%ld} | 从尾部删除最短匹配 ld | hello wor |
${str%%ld} | 从尾部删除最长匹配 ld | hello wor |
${str/world/Shell} | 替换第一个匹配项 | hello Shell |
${str//l/LL} | 替换所有匹配项 | heLLo worLLd |
实战案例:
path="/home/user/documents/report.txt"
# 提取文件名
filename=${path##*/} # 输出:report.txt
# 提取文件类型
extension=${filename##*.} # 输出:txt
6.2 命令替换
语法:
变量=$(命令) # 推荐写法
变量=`命令` # 反引号写法(易混淆)
示例:
# 获取当前日期
date=$(date +%Y-%m-%d)
echo "今天日期:$date" # 输出:今天日期:2023-10-01# 获取文件行数
line_count=$(wc -l < /etc/passwd)
echo "用户文件行数:$line_count" # 输出:用户文件行数:42
7. 常见易错点与解决方案
7.1 变量赋值空格错误
错误示例:
name = "Alice" # 报错:-bash: name: 未找到命令
解决方案:
name="Alice" # 正确写法
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 PI=3.14 # 定义只读变量
PI=3.1415 # 报错:PI: 只读变量
8.2 删除变量
name="Alice"
unset name # 删除变量
echo $name # 输出:(空)
8.3 变量类型转换
num="123"
echo $((num + 100)) # 输出:223(自动转换为整数)
9. 实战案例:变量综合应用
9.1 批量重命名文件
#!/bin/bash
# 将当前目录下所有 .txt 文件重命名为 .log
for file in *.txt; donew_name="${file%.txt}.log" # 替换扩展名mv "$file" "$new_name"echo "重命名:$file → $new_name"
done
9.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_count"
10. 总结:变量的 “生存法则”
- 命名规范:小写字母开头,避免与系统变量冲突。
- 引号使用:值含空格或特殊字符时,优先使用双引号。
- 作用域控制:函数内变量使用
local
声明,避免全局污染。 - 性能优化:算术运算用
$(( ))
,命令替换用$( )
。
通过以上内容,你将掌握 Shell 变量的核心操作,从基础定义到高级应用,再到实战案例,逐步提升脚本编写能力。变量是 Shell 编程的基石,熟练运用它们能让你的脚本更灵活、高效!
四、运算符与条件判断
1. 关系运算符(判断条件)
(1)数字比较
运算符 | 含义 | 示例(a=10 ,b=20 ) |
---|---|---|
-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 orange
3. 遍历数组示例
#!/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.txt"if [ -e "$file" ]; then # 文件存在if [ -f "$file" ]; then # 是普通文件echo "文件 $file 是普通文件"elif [ -d "$file" ]; then # 是目录echo "文件 $file 是目录"else # 其他类型(如链接、设备文件)echo "文件 $file 是特殊文件"fi
elseecho "文件 $file 不存在"
fi
1.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";;
esac
2. 循环结构:让脚本 “重复执行”
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
格式 2:C 语言风格(指定次数)
for ((初始值; 条件; 增量)); do命令 # 按次数循环
done
示例:计算 1+2+…+10
sum=0
for ((i=1; i<=10; i++)); dosum=$((sum + i))
done
echo "总和:$sum" # 输出:55
2.2 while 循环:条件驱动的重复
语法格式:
while [ 条件 ]; do命令 # 条件为真时持续执行
done
示例:逐行读取文件
#!/bin/bash
file="users.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 (仅打印 1) |
continue | 跳过当前循环迭代 | 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 # 换行
done
4. 函数:代码复用的 “积木”
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 [ $age>18 ]; 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_dir"echo "备份完成!文件路径:$backup_dir/data_$timestamp.tar.gz"
6.2 交互式猜数字游戏
#!/bin/bash
# 生成 1-100 随机数
num=$((RANDOM % 100 + 1))
attempts=0 # 记录尝试次数while true; do # 无限循环,直到猜对read -p "请输入一个数字(1-100):" guessattempts=$((attempts + 1))if [ $guess -eq $num ]; thenecho "恭喜!你在 $attempts 次内猜对了!"break # 跳出循环elif [ $guess -gt $num ]; thenecho "猜大了!再试一次。"elseecho "猜小了!再试一次。"fi
done
7. 拓展知识:让流程控制更强大
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" # 输出:120
7.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" # 输出:计算结果:8
2.2 参数验证:避免无效输入
场景:当函数需要固定数量的参数时,先检查参数个数。
add() {if [ $# -ne 2 ]; then # 检查参数是否为 2 个echo "错误:需要 2 个参数,实际 $# 个"return 1 # 返回错误码filocal sum=$(( $1 + $2 ))echo $sum
}# 调用错误示例
add 5 # 输出:错误:需要 2 个参数,实际 1 个
echo "返回码:$?" # 输出:1
3. 变量作用域:避免 “变量污染”
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 "文件不存在"
fi
4.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:00
5. 高级技巧:让函数更强大
5.1 函数递归:用循环逻辑解决复杂问题
场景:计算阶乘、斐波那契数列等递归问题。
# 计算 n 的阶乘(递归实现)
factorial() {local n=$1if [ $n -eq 0 ]; thenecho 1 # 递归终止条件elseecho $(( $n * $(factorial $((n-1))) )) # 递归调用fi
}result=$(factorial 5)
echo "5 的阶乘:$result" # 输出:120
5.2 默认参数:让函数更 “智能”
语法:通过 ${参数:-默认值}
实现参数默认值。
greet() {local name=${1:-"Guest"} # 若未传参,默认值为 "Guest"echo "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.1
6. 常见易错点与解决方案
6.1 忘记声明局部变量导致全局污染
错误示例:
count=0 # 全局变量
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 "$dir"if [ $? -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_dir"echo "文件 $src 复制到 $dest_dir 成功"return 0
}# 调用函数
copy_file "data.txt" "backup"
7.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" # 调用选择处理函数
done
8. 拓展知识:函数的进阶应用
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%400==0) 或 (year%4==0 且 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
错误,必须为a=10
(等号前后不能有空格)。 - 中括号空格:
[条件]
需写成[ 条件 ]
,否则报错(如[a -gt 5]
错误,应为[ $a -gt 5 ]
)。 - 文件路径错误:执行脚本时需用
./脚本名
,直接输入脚本名
会提示 “命令未找到”。 - 转义符遗漏:使用
expr
计算乘法时,*
需转义为\*
,或改用$(( ))
避免转义。 - 字符串比较误区:比较字符串是否相等时,
=
前后需留空格(如[ "$a" = "$b" ]
),否则会被视为赋值。
十、总结
Shell 编程是 Linux 自动化的核心技能,从简单的脚本到复杂的流程控制,需要通过大量实践掌握。新手入门时,建议:
- 从单个知识点入手,如变量、循环、函数,逐个击破。
- 每学一个语法,编写小例子验证效果,理解背后逻辑。
- 遇到错误时,善用
echo
打印变量值,或用bash -x 脚本名
调试(显示每行执行过程)。
记住:Shell 脚本的魅力在于 “用简单命令组合实现强大功能”,坚持练习,你会逐渐体会到它的高效与便捷!
相关文章:
linux入门六:Linux Shell 编程
一、Shell 概述 1. 什么是 Shell? Shell 是 Linux 系统中用户与内核之间的桥梁,作为 命令解析器,它负责将用户输入的文本命令转换为计算机可执行的机器指令。 本质:Shell 是一个程序(如常见的 Bash、Zsh)…...
Franka 机器人x Dexterity Gen引领遥操作精细任务新时代
教授机器人工具灵活操作难题 在教授机器人灵活使用工具方面,目前主要有两种策略:一是人类遥控(用于模仿学习),二是模拟到现实的强化学习。然而,这两种方法均存在明显的局限性。 1、人类遥控(用…...
网络通讯协议UDP转发TCP工具_UdpToTcpRelay_双向版
UDP/TCP网络转发器程序说明书 1. 程序概述 本程序是一个高性能网络数据转发工具,支持UDP和TCP协议之间的双向数据转发,并具备以下核心功能: 协议转换:实现UDP↔TCP协议转换数据转换:支持十六进制/ASCII格式的数据转…...
深入理解 RxSwift 中的 Driver:用法与实践
目录 前言 一、什么是Driver 1.不会发出错误 2.主线程保证 3.可重放 4.易于绑定 二、Driver vs Observable 三、使用场景 1.绑定数据到UI控件 2.响应用户交互 3.需要线程安全的逻辑 4.如何使用Driver? 1.绑定文本输入到Label 2.处理按钮点击事件 3…...
【XML基础-3】深入理解XML Schema:XML的强大语义约束机制
XML(可扩展标记语言)作为数据交换的标准格式,在当今信息技术领域扮演着重要角色。然而,仅有基本的XML语法规则往往不足以满足复杂的数据验证需求。这正是XML Schema发挥作用的地方——它为XML文档提供了强大的语义约束能力。本文将…...
神经网络语言模型与统计语言模型的比较
神经网络语言模型(Neural Language Models, NLMs)与统计语言模型(Statistical Language Models, SLMs)是自然语言处理(NLP)中两类核心的语言建模方法,其核心差异体现在建模方式、表示能力、数据…...
大模型论文:CRAMMING TRAINING A LANGUAGE MODEL ON ASINGLE GPU IN ONE DAY(效率提升)-final
大模型论文:CRAMMING: TRAINING A LANGUAGE MODEL ON ASINGLE GPU IN ONE DAY(效率提升) 文章地址:https://arxiv.org/abs/2212.14034 摘要 近年来,语言建模的研究趋势集中在通过大规模扩展来提升性能,导致训练语言模型的成本变…...
构建AI应用(持续更新)
常用的框架: dify、coze:低代码模块化编程 langchain:面向程序人员 常规的应用: 语音转文字ASR,文字转语音TTS,下一步问题建议, 旅游计划,买点提取,情感陪聊&#x…...
【JAVA】JVM 堆内存“缓冲空间”的压缩机制及调整方法
1. 缓冲空间是否可压缩? 是的,JVM 会在满足条件时自动收缩堆内存,将未使用的缓冲空间释放回操作系统。但需满足以下条件: GC 触发堆收缩:某些垃圾回收器(如 G1、Serial、Parallel)在 Full GC …...
NLP高频面试题(三十八)——什么是LLM的灾难性遗忘?如何避免灾难性遗忘?
近年来,大语言模型在人工智能领域取得了显著进展。然而,随着模型的不断更新和新任务的引入,出现了一个重要的问题,即灾难性遗忘(Catastrophic Forgetting)。灾难性遗忘指的是大模型在连续学习新知识或新任务时,先前掌握的旧知识会迅速被覆盖或遗忘,从而导致模型在旧任务…...
Keepalived+LVS高可用集群实战:从原理到落地
在分布式系统架构中,服务的高可用性和负载均衡是保障业务连续性的核心要素。本文通过一次实验,深入探索了基于KeepalivedLVS的高可用负载均衡集群方案,带您从零开始理解原理、动手实践配置,并验证其可靠性。 一、实验目标 本次实…...
【JVM】JVM调优实战
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!Ǵ…...
Linux系统安全-开发中注意哪些操作系统安全
Hey小伙伴们~👋 在Linux开发中,确保操作系统的安全真的太太太重要啦!🛡️ 今天就来和大家聊聊几个超关键的注意事项,记得拿小本本记下来哦!📝 1️⃣ 用户管理与权限控制👥 合理…...
Qt问题之 告别软件因系统默认中文输入法导致错误退出的烦恼
1. 问题 使用Qt进行研发时,遇到一个问题,当在系统默认输入法中文(英文输入法或者搜狗就不会触发闪退)的情况下,选中QTableWidget控件(QTableWidgetItem有焦点,但是不双击)ÿ…...
2025 年“认证杯”数学中国数学建模网络挑战赛 D题 无人机送货规划
在快递和外卖等短途递送小件货物的业务中,无人机或许大有可为。现 有一个城市的快递仓库准备使用若干无人机进行派件,设有若干架无人机从 仓库出发,分别装载了若干快递包裹。每架无人机装载的包裹的收货地点会 被排列为一个目的地列表&#x…...
【2025年认证杯数学中国数学建模网络挑战赛】A题解题思路与模型代码
【2025年认证杯数学建模挑战赛】A题 该题为典型的空间几何建模轨道动力学建模预测问题。 ⚙ 问题一:利用多个天文台的同步观测,确定小行星与地球的相对距离 问题分析 已知若干地面天文台的观测数据:方位角 (Azimuth) 和 高度角 (Altitude)&…...
Redhat红帽 RHCE8.0认证体系课程
课程大小:7.7G 课程下载:https://download.csdn.net/download/m0_66047725/90546064 更多资源下载:关注我 红帽企业 Linux 系统的管理技能已经成为现代数据中心的核心竞争力。 Linux 在支持混合云、跨物理服务器、虚机、私有云和公共云计…...
Python 实现的运筹优化系统数学建模详解(最大最小化模型)
一、引言 在数学建模的实际应用里,最大最小化模型是一种极为关键的优化模型。它的核心目标是找出一组决策变量,让多个目标函数值里的最大值尽可能小。该模型在诸多领域,如资源分配、选址规划等,都有广泛的应用。本文将深入剖析最大…...
MySQL快速入门
MySQL快速入门 SQL语句 SQL语句概述 1.SQL 是用于访问和处理数据库的标准的计算机语言。 2.SQL指结构化查询语言,全称是 Structured Query Language。 3.SQL 可以访问和处理数据库。 4.SQL 是一种 ANSI(American National Standards Institute 美国…...
离线安装 nvidia-docker2(nvidia-container-toolkit)
很多时候大家都有用docker使用gpu的需求,但是因为网络等原因不是那么好用,这里留了一个给ubuntu的安装包,网络好的话也提供了在线安装方式 安装 nvidia-docker2 1 离线安装 (推荐) unzip解压后进入目录 dpkg -i *.d…...
【自然语言处理】深度学习中文本分类实现
文本分类是NLP中最基础也是应用最广泛的任务之一,从无用的邮件过滤到情感分析,从新闻分类到智能客服,都离不开高效准确的文本分类技术。本文将带您全面了解文本分类的技术演进,从传统机器学习到深度学习,手把手实现一套…...
云原生运维在 2025 年的发展蓝图
随着云计算技术的不断发展和普及,云原生已经成为了现代应用开发和运维的主流趋势。云原生运维是指在云原生环境下,对应用进行部署、监控、管理和优化的过程。在 2025 年,云原生运维将迎来更加广阔的发展前景,同时也将面临着一系列…...
Windows系统Python多版本运行解决TensorFlow安装问题(附详细图文)
Windows系统Python多版本运行解决TensorFlow安装问题(附详细图文) 摘要 TensorFlow 无法安装?Python版本太高是元凶! 本文针对Windows系统中因Python版本过高导致TensorFlow安装失败的问题,提供三种降级解决方案&…...
银行业务知识序言
银行业务知识体系全景解析 第一章 金融创新浪潮下的银行业务知识革命 1.1 数字化转型驱动金融业态重构 在区块链、人工智能、物联网等技术的叠加作用下,全球银行业正经历着"服务无形化、流程智能化、风控穿透化"的深刻变革。根据麦肯锡《2023全球银行业…...
《深度剖析分布式软总线:软时钟与时间同步机制探秘》
在分布式系统不断发展的进程中,设备间的协同合作变得愈发紧密和复杂。为了确保各个设备在协同工作时能够有条不紊地进行,就像一场精准的交响乐演出,每个乐器都要在正确的时间奏响音符,分布式软总线中的软时钟与时间同步机制应运而…...
RK3588 android12 适配 ilitek i2c接口TP
一,Ilitek 触摸屏简介 Ilitek 提供多种型号的触控屏控制器,如 ILI6480、ILI9341 等,采用 I2C 接口。 这些控制器能够支持多点触控,并具有优秀的灵敏度和响应速度。 Ilitek 的触摸屏控制器监测屏幕上的触摸事件。 当触摸发生时&a…...
pgsql:关联查询union(并集)、except(差集)、intersect(交集)
pgsql:关联查询union(并集)、except(差集)、intersect(交集)_pgsql except-CSDN博客...
模型材质共享导致的问题
问题:当我选中其中某个网格模型并设置color的时候,相同种类的颜色都被改变,但是打印我选中的网格模型数据其实只有一个。 导致问题的原因: 加载Blender模型修改材质颜色 Blender创建一个模型对象,设置颜色࿰…...
ThinkpPHP生成二维码
导入依赖 composer require endroid/qr-code 封装成函数,传入二维码包含的值,存储路径,二维码大小,二维码边距 private function getCode($content, $directory, $size 300, $margin 10){// 创建二维码对象// $content: 二…...
FLINK框架:流式处理框架Flink简介
在大数据时代,数据的价值不言而喻,谁能利用好数据,谁就掌握了整个行业的先机。面对海量的数据,如何处理数据成为了一个难题。除了海量数据外,实时性也是一个重要的课题,所以流式数据处理便登上了技术舞台&a…...
使用Python从零开始构建生成型TransformerLM并训练
在人工智能的浩瀚宇宙中,有一种神奇的生物,它拥有着强大的语言魔法,能够生成各种各样的文本,仿佛拥有无尽的创造力。它就是——Transformer 模型!Transformer 模型的出现,为人工智能领域带来了一场“语言魔…...
xtrabackup备份
安装: https://downloads.percona.com/downloads/Percona-XtraBackup-8.0/Percona-XtraBackup-8.0.35-30/binary/tarball/percona-xtrabackup-8.0.35-30-Linux-x86_64.glibc2.17.tar.gz?_gl1*1ud2oby*_gcl_au*MTMyODM4NTk1NS4xNzM3MjUwNjQ2https://downloads.perc…...
2.3 Spark运行架构与流程
Spark运行架构与流程包括几个核心概念:Driver负责提交应用并初始化作业,Executor在工作节点上执行任务,作业是一系列计算任务,任务是作业的基本执行单元,阶段是一组并行任务。Spark支持多种运行模式,包括单…...
【Pandas】pandas DataFrame head
Pandas2.2 DataFrame Indexing, iteration 方法描述DataFrame.head([n])用于返回 DataFrame 的前几行 pandas.DataFrame.head pandas.DataFrame.head 是一个方法,用于返回 DataFrame 的前几行。这个方法非常有用,特别是在需要快速查看 DataFrame 的前…...
从递归入手一维动态规划
从递归入手一维动态规划 1. 509. 斐波那契数 1.1 思路 递归 F(i) F(i-1) F(i-2) 每个点都往下展开两个分支,时间复杂度为 O(2n) 。 在上图中我们可以看到 F(6) F(5) F(4)。 计算 F(6) 的时候已经展开计算过 F(5)了。而在计算 F(7)的时候,还需要…...
鸿蒙HarmonyOS埋点SDK,ClkLog适配鸿蒙埋点分析
ClkLog埋点分析系统,是一种全新的、开源的洞察方案,它能够帮助您捕捉每一个关键数据点,确保您的决策基于最准确的用户行为分析。技术人员可快速搭建私有的分析系统。 ClkLog鸿蒙埋点SDK通过手动埋点的方式实现HarmonyOS 原生应用的前端数据采…...
HarmonyOS:HMPermission权限请求框架
前段时间利用空余时间写了一个权限请求库:HMPermission。 一,简介 HMPermission 是鸿蒙系统上的一款权限请求框架,封装了权限请求逻辑,采用链式调用的方式请求权限,简化了权限请求的代码。 二,使用方法 …...
【书籍】DeepSeek谈《持续交付2.0》
目录 一、深入理解1. 核心理念升级:从"自动化"到"双环模型"2. 数字化转型的五大核心能力3. 关键实践与案例4. 组织与文化变革5. 与其它框架的关系6. 实际应用建议 二、对于开发实习生的帮助1. 立刻提升你的代码交付质量(技术验证环实…...
Spring AOP 扫盲
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
银河麒麟v10(arm架构)部署Embedding模型bge-m3【简单版本】
硬件 服务器配置:鲲鹏2 * 920(32c) 4 * Atlas300I duo卡 参考文章 https://www.hiascend.com/developer/ascendhub/detail/07a016975cc341f3a5ae131f2b52399d 鲲鹏昇腾Atlas300Iduo部署Embedding模型和Rerank模型并连接Dify(自…...
如何通过流程管理优化企业运营?
流程管理的本质是“用确定性的规则应对不确定性的业务”。 那么,具体该如何通过流程管理来优化企业的运作呢?以下是一些关键步骤和思路,或许能给到一些启发。 1. 从流程梳理开始:摸清现状,找准问题 想要管理好企业的…...
ZYNQ笔记(四):AXI GPIO
版本:Vivado2020.2(Vitis) 任务:使用 AXI GPIO IP 核实现按键 KEY 控制 LED 亮灭(两个都在PL端) 一、介绍 AXI GPIO (Advanced eXtensible Interface General Purpose Input/Output) 是 Xilinx 提供的一个可…...
Java学习手册:JVM、JRE和JDK的关系
在Java生态系统中,JVM(Java虚拟机)、JRE(Java运行时环境)和JDK(Java开发工具包)是三个核心概念。它们共同构成了Java语言运行和开发的基础。理解它们之间的关系对于Java开发者来说至关重要。本文…...
Java 并发-newFixedThreadPool
前言 为什么选择使用多线程?一种场景是在数据和业务处理能力出现瓶颈时,而服务器性能又有空闲,通常是cpu空闲,这时使用多线程就能很好的解决问题,而又无需加硬件,实际使用中,线程池又是最为常用…...
C# task任务异步编程提高UI的响应性
方式1:async/await模式 private async void button1_Click(object sender, EventArgs e){try{var result await Task.Run(() > CalculateResult());label1.Text result.ToString();}catch (Exception ex){label1.Text $"Error: {ex.Message}";}}pri…...
Spring Bean生命周期执行流程详解
文章目录 一、什么是Spring Bean生命周期?工作流程图:二、Bean生命周期执行流程验证1.编写测试代码验证结果2.源码追溯Bean初始化回调过程 一、什么是Spring Bean生命周期? Spring Bean生命周期是指从Bean的创建到销毁的整个过程,…...
windows 安装 pygame( pycharm)
一、安装流程 1.查看python版本 2.检查是否安装pip 3.下载pygame安装文件 下载地址:https://pypi.org/project/pygame/#files 选择合适的版本(我选择的是 python3.7 windows 64bit): 4.使用pip安装pygame 将下载好的whl文件移动到…...
Envoy网关实例异常重启排查总结
一、事件背景 于10月24日凌晨业务租户有业务应用发版上线,中午收到pod连续5分钟重启严重告警,登录管理节点查看异常重启的应用网关pod日志,存在内核段错误报错信息导致进程终止并触发监控检查异常并重启; 该报错主要是访问的内存超出了系统…...
WinForm真入门(13)——ListBox控件详解
WinForm ListBox 详解与案例 一、核心概念 ListBox 是 Windows 窗体中用于展示可滚动列表项的控件,支持单选或多选操作,适用于需要用户从固定数据集中选择一项或多项的场景。 二、核心属性 属性说明Items管理列表项的集合,支持动…...
【Linux网络编程】UDP Echo Server的实现
本文专栏:Linux网络编程 目录 一,Socket编程基础 1,IP地址和端口号 端口号划分范围 理解端口号和进程ID 源端口号和目的端口号 理解Socket 2,传输层的典型代表 3,网络字节序 4,Socket编程接口 s…...