嵌入式Linux——Shell脚本编程

Shell 脚本编程是通过**编写一系列 Shell 命令的集合(文本文件),实现自动化任务的技术。**它广泛用于系统管理、批量处理、自动化部署等场景。以下是 Shell 脚本编程的核心知识点和入门指南:

一、基础概念

  • 什么是 Shell 脚本 :由 Shell 命令组成的文本文件,可直接被 Shell 解释器(如bashsh)执行,无需编译。
  • 常用 Shellbash(最流行,Linux 默认)、sh(基础版)、zsh(增强版)等,本文以bash为例。

二、脚本基本结构与运行

1. 脚本格式

脚本第一行必须指定解释器("shebang"),后续为命令序列:

复制代码
#!/bin/bash  # 指定用bash解释器执行
# 这是注释(#开头的行)
echo "Hello, Shell Script!"  # 输出内容

2. 运行脚本的步骤

  • 创建脚本文件 :用vimnano等编辑器创建,例如test.sh

  • 赋予执行权限

    复制代码
    chmod +x test.sh  # 允许脚本执行
  • 执行脚本

    复制代码
    ./test.sh  # 当前目录执行(需权限)
    # 或直接指定解释器:bash test.sh(无需执行权限)

三、核心语法

1. 变量

变量用于存储数据,分为系统变量 (预定义)和自定义变量

  • 系统变量 :如$HOME(用户主目录)、$PATH(命令搜索路径)、$USER(当前用户)等。示例:echo "当前用户:$USER"

  • 自定义变量

    • 赋值:变量名=值等号前后无空格 ),例如name="Alice"
    • 使用:$变量名${变量名}(推荐,避免歧义),例如echo "Name: $name"
    • 只读变量:readonly age=18(赋值后不可修改)。
    • 删除变量:unset name(只读变量不可删除)。
  • 特殊变量(处理脚本参数):

    • $0:脚本文件名(如test.sh)。
    • $1~$9:第 1 到第 9 个参数(超过 9 个用${10})。
    • $#:参数总个数。
    • $*/$@:所有参数($*将参数视为整体,$@视为独立个体)。
    • $?:上一条命令的退出状态(0 表示成功,非 0 表示失败)。

    示例(param.sh):

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

    运行:./param.sh a b c,输出:

    复制代码
    脚本名:./param.sh
    参数1:a
    参数总数:3
    所有参数:a b c

2. 输入输出

  • 输出echo命令,支持转义字符(需加-e)。

    复制代码
    echo "Hello"  # 输出Hello(默认换行)
    echo -n "Hello"  # 不换行输出
    echo -e "Line1\nLine2"  # 换行输出两行
  • 输入read命令读取用户输入。

    复制代码
    #!/bin/bash
    read -p "请输入姓名:" name  # -p显示提示信息
    echo "你好,$name!"

    运行后输入姓名,会输出对应问候。

3.逻辑与和逻辑或

在 Shell 脚本中,逻辑与(AND) 逻辑或(OR)用于组合多个条件或命令,实现更复杂的逻辑判断。它们在条件判断命令执行流程中有着不同的语法和特性,以下是详细说明:

一、逻辑与(AND)的用法

逻辑与表示 "多个条件同时成立" 或 "前一个命令成功后执行下一个命令"。

1. 在test/[ ]中:用-a

test命令或[ ]结构中,逻辑与用-a表示,格式为:

复制代码
test 条件1 -a 条件2
[ 条件1 -a 条件2 ]

示例:判断文件存在且可写

复制代码
if [ -e "file.txt" -a -w "file.txt" ]; then
  echo "文件存在且可写"
fi
2. 在[[ ]]中:用&&

[[ ]]是 Bash 的增强语法,逻辑与用&&表示,更直观:

复制代码
[[ 条件1 && 条件2 ]]

示例:判断字符串非空且数值大于 10

复制代码
str="hello"
num=20
if [[ -n "$str" && $num -gt 10 ]]; then
  echo "条件成立"
fi
3. 在命令执行中:用&&

多个命令用&&连接时,** 前一个命令成功(退出状态 0)** 才会执行后一个命令(短路逻辑)。示例:创建目录并进入(确保 mkdir 成功后再 cd)

复制代码
mkdir "new_dir" && cd "new_dir"

二、逻辑或(OR)的用法

逻辑或表示 "多个条件至少一个成立" 或 "前一个命令失败后执行下一个命令"。

1. 在test/[ ]中:用-o

test[ ]中,逻辑或用-o表示:

复制代码
test 条件1 -o 条件2
[ 条件1 -o 条件2 ]

示例:判断文件是 txt 或 sh 格式

复制代码
if [ "$file" = "*.txt" -o "$file" = "*.sh" ]; then
  echo "是文本或脚本文件"
fi
2. 在[[ ]]中:用||

[[ ]]中逻辑或用||表示:

复制代码
[[ 条件1 || 条件2 ]]

示例:判断数值小于 0 或大于 100

复制代码
num=150
if [[ $num -lt 0 || $num -gt 100 ]]; then
  echo "数值超出范围"
fi
3. 在命令执行中:用||

多个命令用||连接时,** 前一个命令失败(退出状态非 0)** 才会执行后一个命令(短路逻辑)。示例:如果文件不存在则创建

复制代码
ls "file.txt" || touch "file.txt"

三、短路逻辑的特性

  • 逻辑与(&&:若第一个条件 / 命令失败,直接跳过后续判断 / 命令("短路")。
  • 逻辑或(||:若第一个条件 / 命令成功,直接跳过后续判断 / 命令("短路")。

示例:结合逻辑与或实现复杂判断

复制代码
# 判断文件存在且(是txt或sh格式)
if [ -e "file" ] && ( [ "$file" = "*.txt" ] || [ "$file" = "*.sh" ] ); then
  echo "符合条件"
fi

四、注意事项

  1. [ ][[ ]]的语法差异

    • [ ]中使用-a/-o,且条件内的变量建议加双引号(避免空值报错)。
    • [[ ]]中使用&&/||,支持更直观的逻辑,且变量引用可不加引号(但建议加)。
  2. 命令间的逻辑与或

    • &&||可用于连接多个命令,实现 "依赖执行" 或 "兜底执行"。
  3. 优先级 :逻辑非(!)> 逻辑与(-a/&&)> 逻辑或(-o/||),复杂逻辑建议用括号明确优先级。

4.1 条件判断(if 语句)

格式:

复制代码
if [ 条件表达式 ]; then
  # 条件为真时执行
elif [ 条件表达式 ]; then
  # 否则若该条件为真时执行
else
  # 所有条件为假时执行
fi

条件表达式规则

  • 中括号[ ]前后必须有空格。
  • 数值比较:-eq(等于)、-ne(不等于)、-gt(大于)、-lt(小于)、-ge(大于等于)、-le(小于等于)。例:[ $a -gt 10 ](a 大于 10)。
  • 字符串比较:=(等于)、!=(不等于)、-z(空字符串)、-n(非空字符串)。例:[ "$str" = "hello" ](字符串相等,变量加引号避免空值报错)。
  • 文件判断:-f(普通文件)、-d(目录)、-r(可读)、-w(可写)、-x(可执行)、-e(存在)。例:[ -f "file.txt" ](file.txt 是普通文件)。

示例(判断文件是否存在):

复制代码
#!/bin/bash
read -p "输入文件名:" file
if [ -e "$file" ]; then
  echo "$file 存在"
  if [ -f "$file" ]; then
    echo "是普通文件"
  elif [ -d "$file" ]; then
    echo "是目录"
  fi
else
  echo "$file 不存在"
fi

4.2 多分支条件判断(case in 语句)

在 Shell 脚本中,case in语句用于多分支条件判断 ,功能类似于其他编程语言的switch-case语句,适合处理变量与多个固定值 / 模式匹配的场景,语法简洁且可读性强。

一、基本语法结构

复制代码
case $变量 in
  模式1)
    命令序列1
    ;;  # 双分号表示分支结束(无需break,自动退出case)
  模式2)
    命令序列2
    ;;
  模式N)
    命令序列N
    ;;
  *)  # 通配符*表示"默认分支"(所有未匹配的情况)
    命令序列默认
    ;;
esac  # case的结束标记

二、模式匹配规则

case in模式支持通配符,规则与 Shell 文件名匹配一致:

  • *:匹配任意字符(包括空字符)。
  • ?:匹配单个字符
  • [字符范围]:匹配指定范围 / 集合的单个字符 (如[a-z]匹配小写字母)。
  • 多个模式可通过|分隔(表示 "或"),如模式1|模式2)

三、使用示例

示例 1:菜单选择

根据用户输入的数字执行对应操作:

复制代码
#!/bin/bash
read -p "请选择操作(1-3):" choice

case $choice in
  1)
    echo "执行操作1:创建文件"
    touch new_file.txt
    ;;
  2)
    echo "执行操作2:删除文件"
    rm -f old_file.txt
    ;;
  3)
    echo "执行操作3:查看文件"
    ls -l
    ;;
  *)
    echo "输入无效,请选择1-3!"
    ;;
esac
示例 2:文件类型判断

根据文件扩展名执行不同处理:

复制代码
#!/bin/bash
file="report.pdf"
ext=${file##*.}  # 提取文件扩展名(如pdf、txt)

case $ext in
  txt|md)  # 匹配txt或md格式
    echo "文本文件,使用cat查看"
    cat "$file"
    ;;
  pdf|jpg|png)  # 匹配pdf、图片格式
    echo "多媒体文件,无法直接查看"
    ;;
  *)
    echo "未知格式:$ext"
    ;;
esac
示例 3:通配符匹配

处理带通配符的输入:

复制代码
#!/bin/bash
read -p "输入一个字符:" char

case $char in
  [a-z])  # 匹配小写字母
    echo "你输入了小写字母:$char"
    ;;
  [A-Z])  # 匹配大写字母
    echo "你输入了大写字母:$char"
    ;;
  [0-9])  # 匹配数字
    echo "你输入了数字:$char"
    ;;
  ?)  # 匹配单个非空字符(如符号)
    echo "你输入了符号:$char"
    ;;
  "")  # 匹配空输入(无内容)
    echo "你没有输入任何内容"
    ;;
esac

四、关键特性与注意事项

  1. 自动退出分支 :每个分支的;;会让case执行完命令后直接退出,无需像其他语言一样写break
  2. 变量引用case后的变量直接写$变量,模式中无需加 $ 符号
  3. 默认分支的必要性*)分支建议保留,确保所有未匹配的情况都有处理逻辑,避免脚本异常。
  4. 模式顺序 :若多个模式存在包含关系(如*和具体值),需将具体模式放在前面 ,否则会被*提前匹配。

五、与if-elif-else的对比

场景 case in if-elif-else
多固定值匹配 语法简洁,可读性高 分支多时有冗余,可读性差
复杂条件(如范围) 依赖通配符,灵活性有限 支持数值 / 逻辑运算,灵活性高
执行效率 内部是哈希表匹配,效率略高 逐分支判断,效率随分支数降低

5.test语句

在 Shell 脚本中,test命令是用于条件检测的基础工具,[ ](中括号)本质上是test命令的语法糖(符号链接) ,二者功能几乎完全一致。补充test命令的用法,能更全面理解 Shell 的条件判断机制。

一、test命令的基本用法

test命令用于检测条件是否成立,语法为:

复制代码
test 条件表达式
  • 若条件成立,test返回退出状态码 0(表示 "真");
  • 若条件不成立,返回非 0 状态码(表示 "假")。

通常结合ifwhile等流程控制语句使用,例如:

复制代码
# 判断文件是否存在
if test -f "file.txt"; then
  echo "file.txt存在"
fi

二、test[ ]的等价性

[ ]test的简写形式,二者完全等价。例如:

复制代码
# 以下两种写法效果一致
test -f "file.txt"
[ -f "file.txt" ]  # 注意:[ 后和 ] 前必须有空格(因为[是命令,]是它的最后一个参数)

因此,之前提到的文件测试、字符串测试、数值测试 等条件表达式,既可以用test,也可以用[ ],语法规则完全相同。

三、test支持的条件表达式分类

1. 文件测试(检测文件 / 目录属性)
表达式 含义 test写法示例
-e 文件 文件 / 目录是否存在 test -e "test.sh"
-f 文件 是否为普通文件(非目录 / 设备文件) test -f "data.txt"
-d 目录 是否为目录 test -d "backup/"
-r 文件 是否有读权限 test -r "config.ini"
-w 文件 是否有写权限 test -w "log.txt"
-x 文件 是否有执行权限(或目录是否可进入) test -x "script.sh"
-s 文件 文件是否存在且非空(大小 > 0) test -s "result.log"
文件1 -nt 文件2 文件 1 是否比文件 2 新(修改时间更新) test "a.txt" -nt "b.txt"
文件1 -ot 文件2 文件 1 是否比文件 2 旧(修改时间更早) test "a.txt" -ot "b.txt"
2. 字符串测试(检测字符串属性)
表达式 含义 test写法示例
字符串 字符串是否非空(非空为真) test "hello"(真)、test ""(假)
-z 字符串000 字符串是否为空(空为真) test -z ""(真)、test -z "a"(假)
-n 字符串 字符串是否非空(非空为真,同直接写字符串) test -n "hello"(真)
字符串1 = 字符串2 两字符串是否相等(=可换为==,但=更兼容) test "abc" = "abc"(真)
字符串1 != 字符串2 两字符串是否不相等 test "abc" != "def"(真)
3. 数值测试(比较整数)
表达式 含义 test写法示例
数值1 -eq 数值2 等于(equal) test 5 -eq 5(真)
数值1 -ne 数值2 不等于(not equal) test 5 -ne 3(真)
数值1 -gt 数值2 大于(greater than) test 10 -gt 5(真)
数值1 -lt 数值2 小于(less than) test 3 -lt 7(真)
数值1 -ge 数值2 大于等于(>=) test 5 -ge 5(真)
数值1 -le 数值2 小于等于(<=) test 4 -le 6(真)
4. 逻辑组合(多条件判断)

test支持通过逻辑运算符组合多个条件,常用运算符:

  • !:逻辑非(取反);
  • -a:逻辑与(两个条件都成立才为真);
  • -o:逻辑或(至少一个条件成立为真)。

示例:

复制代码
# 判断文件存在且有写权限(逻辑与)
test -e "file.txt" -a -w "file.txt"

# 判断字符串非空或数值大于10(逻辑或)
test -n "$str" -o 20 -gt 10

# 判断文件不存在(逻辑非)
test ! -e "nonexist.txt"

四、test[[ ]]的区别(扩展)

Bash 支持更强大的[[ ]](双中括号),它是test/[ ]的增强版,语法更灵活,支持:

  1. 直接使用&&(与)、||(或)代替-a-o,逻辑更清晰;
  2. 字符串比较支持模式匹配(如*?通配符);
  3. 不需要对特殊字符(如<>)转义;
  4. 变量引用时,即使为空也不易报错(无需强制加引号,但建议加)。

示例:

复制代码
# [[ ]]中使用&&代替-a
[[ -f "file.txt" && -r "file.txt" ]]

# 模式匹配:判断字符串是否以".sh"结尾
str="script.sh"
[[ $str == *.sh ]]  # 成立(真)

# 数值比较可直接用>、<(无需-gt/-lt)
[[ 10 > 5 ]]  # 成立(真)

注意:[[ ]]是 Bash 的扩展语法,兼容性不如test/[ ](如在sh解释器中可能不支持),脚本需兼容多种 Shell 时建议用test/[ ]

五、实战示例:用test完善脚本

基于之前的 "文件备份脚本",用test改写条件判断部分:

复制代码
#!/bin/bash
# 用test命令完善的备份脚本

if test $# -ne 1; then  # 判断参数个数是否为1
  echo "用法:$0 <要备份的目录>"
  exit 1
fi

src_dir=$1
if test ! -d "$src_dir"; then  # 判断目录是否不存在
  echo "错误:$src_dir 不是目录或不存在"
  exit 1
fi

# 后续逻辑不变...

test命令总结

  • test是 Shell 条件检测的基础命令,[ ]是其简写,二者完全等价;
  • 支持文件、字符串、数值三类测试,以及逻辑组合(!-a-o);
  • [[ ]]是 Bash 增强版,语法更灵活,但兼容性稍弱;
  • 实际使用中,[ ]因写法简洁更常用,test多用于强调可读性的场景。

6. 循环结构

(1)for 循环

遍历列表或范围:

复制代码
# 遍历列表
for fruit in apple banana orange; do
  echo "水果:$fruit"
done

# 遍历数字范围(bash支持)
for i in {1..5}; do
  echo "数字:$i"
done

# C语言风格(bash支持)
for ((i=0; i<3; i++)); do
  echo "i=$i"
done

(2)while 循环

条件为真时重复执行:

复制代码
#!/bin/bash
count=1
while [ $count -le 3 ]; do
  echo "计数:$count"
  count=$((count + 1))  # 数值计算($((表达式)))
done

读取文件每一行:

复制代码
# 读取file.txt的每一行并输出
while read line; do
  echo "行内容:$line"
done < file.txt  # < 重定向输入

(3)until 循环

条件为假时重复执行(与 while 相反):

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

7. 函数

封装可复用的代码块:

复制代码
# 定义函数(两种格式)
function hello() {
  echo "Hello, $1!"  # $1是函数的第一个参数
}

# 或简化为:
greet() {
  local name=$1  # local声明局部变量(仅函数内有效)
  echo "欢迎,$name!"
  return 0  # 返回值(0-255,默认最后一条命令的状态)
}

# 调用函数
hello "Tom"  # 输出:Hello, Tom!
greet "Jerry"  # 输出:欢迎,Jerry!
echo "greet函数返回值:$?"  # 输出0(成功)

8. 管道与重定向

  • 管道(|) :将前一个命令的输出作为后一个命令的输入。例:ls -l | grep ".txt"(查找当前目录的 txt 文件)。

  • 重定向

    • >:覆盖写入文件(例:echo "test" > file.txt)。
    • >>:追加写入文件(例:echo "new line" >> file.txt)。
    • <:从文件读取输入(例:cat < file.txt)。
    • 2>:重定向错误输出(例:ls error.txt 2> err.log,错误写入 err.log)。
    • &>:同时重定向标准输出和错误(例:command &> output.log)。

四、实战示例:文件备份脚本

功能:将指定目录备份到backup文件夹,文件名含当前日期。

复制代码
#!/bin/bash
# 备份脚本:backup.sh

# 检查参数(需传入要备份的目录)
if [ $# -ne 1 ]; then
  echo "用法:$0 <要备份的目录>"
  exit 1  # 非0退出表示失败
fi

src_dir=$1
# 检查目录是否存在
if [ ! -d "$src_dir" ]; then
  echo "错误:$src_dir 不是目录或不存在"
  exit 1
fi

# 备份文件名(例:20251111_backup.tar.gz)
backup_name=$(date +%Y%m%d)_backup.tar.gz
# 创建备份目录(若不存在)
mkdir -p backup
# 打包备份
tar -czf "backup/$backup_name" "$src_dir"

# 检查备份是否成功
if [ $? -eq 0 ]; then
  echo "备份成功:backup/$backup_name"
else
  echo "备份失败"
  exit 1
fi

运行:./backup.sh ~/Documents,会在当前目录的backup文件夹生成带日期的压缩包。

五、常见问题

  • 脚本执行报错 "Permission denied":未赋予执行权限,需chmod +x 脚本名
  • 变量引用报错:赋值时等号前后有空格,或字符串比较未加引号(空变量会导致语法错误)。
  • 条件判断报错:中括号[ ]前后缺少空格,或使用了错误的比较符号(如数值用>而非-gt)。
相关推荐
HLC++11 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室12 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy15 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕15 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫16 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生17 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1118 小时前
linux基础IO
linux·运维·服务器
BelongPanda20 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯20 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983420 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器