嵌入式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)。
相关推荐
大志若愚YYZ2 小时前
嵌入式Linux学习——环境变量与配置文件的关系(⭐难理解)
linux·学习
香吧香2 小时前
SNMP 请求响应报文传输分片定位
linux·网络与传输协议
Bowen_CV2 小时前
Linux 系统安装与环境配置实践
linux·运维·服务器
无泊里3 小时前
linux:系统用户命令
linux·运维·服务器
Fcy6484 小时前
Linux下的项目自动化构建-make\makefile详解
linux·运维·自动化·makefile·make
chde2Wang4 小时前
Linux中bash: ls: 未找到命令… 相似命令是: ‘lz‘
linux·运维·bug·bash
楼田莉子5 小时前
Linux学习:进程的控制
linux·运维·服务器·c语言·后端·学习
JiMoKuangXiangQu5 小时前
Linux:文件 mmap 读写流程简析
linux·内存管理·file mmap
洋芋土豆6 小时前
linux用户及权限管理
linux·运维·服务器