NuttX 命令行原生脚本文件完全指南

引言

在嵌入式开发中,脚本文件是自动化任务的重要工具。NuttX 作为一款轻量级、POSIX 兼容的实时操作系统,同样提供了强大的脚本支持能力。本文将深入介绍 NuttX 命令行下原生脚本文件的功能和使用方法,帮助开发者充分利用脚本提升开发效率。


一、NuttX 脚本系统概述

1.1 NSH 脚本解释器

NuttX 的脚本支持是通过 NuttShell (NSH) 实现的。NSH 是一个专为嵌入式系统设计的轻量级命令行解释器,具备以下核心特点:

  • 轻量级设计:占用内存少,适合资源受限的嵌入式环境
  • POSIX 兼容:命令语法与 Unix/Linux Shell 高度相似
  • 脚本支持:支持 .sh 脚本文件的解析和执行
  • 可配置性:可通过 Kconfig 灵活启用或禁用脚本功能

1.2 脚本系统架构

交互式命令

脚本文件

文件结束

用户输入

NSH 命令行

命令类型判断

直接执行

脚本解释器

逐行解析

变量替换

命令展开

管道处理

重定向处理

命令执行

脚本退出


二、脚本文件基础

2.1 脚本文件格式

NuttX 脚本文件遵循以下基本格式:

#!/bin/sh
# 脚本注释

# 命令序列
echo "Hello, NuttX!"
ls -l /

关键要素说明

要素 说明 示例
Shebang 指定脚本解释器 #!/bin/sh
注释 # 开头的注释行 # 这是注释
命令 NSH 支持的命令 echo, ls, cd
变量 使用 $ 引用变量 $PATH, ${VAR}

2.2 脚本创建方法

方法一:使用 cat 命令创建

nsh> cat > myscript.sh << EOF
#!/bin/sh
echo "Hello from script"
ls -l /
EOF

方法二:使用编辑器创建

nsh> vi myscript.sh

方法三:从宿主机传输

# 在宿主机上
$ cp myscript.sh /mnt/nuttx/

# 或通过串口传输
$ minicom -b 115200 -D /dev/ttyUSB0

2.3 脚本执行方式

方式一:使用 sh 命令执行

nsh> sh myscript.sh

方式二:直接执行(需可执行权限)

nsh> chmod 755 myscript.sh
nsh> ./myscript.sh

方式三:使用 source 命令(在当前 Shell 环境执行)

nsh> source myscript.sh

三种方式对比

方式 特点 适用场景
sh script.sh 在子 Shell 中执行,不影响当前环境 独立任务脚本
./script.sh 需要可执行权限,子 Shell 执行 独立任务脚本
source script.sh 在当前 Shell 环境执行,影响当前环境 环境配置脚本

三、NSH 脚本核心功能

3.1 变量支持

环境变量

# 查看所有环境变量
nsh> set

# 设置环境变量
nsh> set MY_VAR=hello

# 使用环境变量
nsh> echo $MY_VAR
hello

# 删除环境变量
nsh> unset MY_VAR

脚本变量

#!/bin/sh
# 定义变量
NAME="NuttX"
VERSION="12.0"

# 使用变量
echo "系统名称: $NAME"
echo "系统版本: $VERSION"

# 变量替换
echo "$NAME 版本 $VERSION"

# 变量计算
COUNT=10
echo "计数: ${COUNT}"

特殊变量

变量 含义 示例
$0 脚本名称 myscript.sh
$1-$9 位置参数 命令行参数
$# 参数数量 参数个数
$@ 所有参数 完整参数列表
$? 上一条命令返回值 0 表示成功

3.2 命令行参数

#!/bin/sh
# 带参数的脚本示例

echo "脚本名称: $0"
echo "参数数量: $#"
echo "所有参数: $@"

# 遍历参数
for i in "$@"
do
    echo "参数: $i"
done

# 判断参数数量
if [ $# -lt 2 ]; then
    echo "用法: $0 <参数1> <参数2>"
    exit 1
fi

echo "第一个参数: $1"
echo "第二个参数: $2"

执行示例

nsh> sh args.sh hello world
脚本名称: args.sh
参数数量: 2
所有参数: hello world
参数: hello
参数: world
第一个参数: hello
第二个参数: world

3.3 重定向功能

输出重定向

# 覆盖写入文件
nsh> echo "hello" > output.txt

# 追加写入文件
nsh> echo "world" >> output.txt

# 错误重定向
nsh> nonexistent_cmd 2> error.log

# 同时重定向标准输出和错误
nsh> cmd > output.log 2>&1

输入重定向

# 从文件读取输入
nsh> cat < input.txt

# 脚本中使用
#!/bin/sh
echo "请输入内容:"
read LINE
echo "您输入了: $LINE"

3.4 管道功能

# 基本管道
nsh> ls / | grep "bin"

# 多级管道
nsh> cat file.txt | grep "error" | wc -l

# 脚本中的管道
#!/bin/sh
echo "系统进程数量:"
ps | grep -v "PID" | wc -l

3.5 命令组合

# 分号分隔(顺序执行)
nsh> cmd1; cmd2; cmd3

# && 组合(前一个成功才执行下一个)
nsh> mkdir -p /tmp/data && echo "创建成功"

# || 组合(前一个失败才执行下一个)
nsh> cd /nonexistent || echo "目录不存在"

# 脚本中的命令组合
#!/bin/sh
echo "开始执行..."
mkdir -p /tmp/data && echo "目录创建成功" || echo "目录创建失败"
echo "执行完毕"

3.6 通配符支持

# * 匹配任意字符
nsh> ls *.txt

# ? 匹配单个字符
nsh> ls file?.txt

# [] 匹配字符集
nsh> ls file[123].txt

# 脚本中的通配符
#!/bin/sh
echo "列出所有文本文件:"
ls *.txt

echo "\n列出所有日志文件:"
ls *.log

四、常用脚本文件示例

4.1 系统初始化脚本

文件名init.sh

功能:系统启动时自动执行,完成目录创建、环境变量设置、服务启动等初始化工作。

#!/bin/sh
# 系统初始化脚本
# 作者: NuttX Developer
# 日期: 2024-01-01

echo "[INIT] ==================================="
echo "[INIT]  NuttX 系统初始化脚本"
echo "[INIT] ==================================="

# 创建必要的目录结构
echo "[INIT] 创建目录结构..."
mkdir -p /tmp/data
mkdir -p /tmp/logs
mkdir -p /tmp/config
mkdir -p /mnt/flash
mkdir -p /mnt/sdcard

# 设置环境变量
echo "[INIT] 设置环境变量..."
set PATH=/bin:/usr/bin:/sbin:/usr/sbin
set HOME=/root
set LOG_DIR=/tmp/logs
set DATA_DIR=/tmp/data

# 挂载文件系统
echo "[INIT] 挂载文件系统..."
mount -t tmpfs none /tmp
if [ $? -eq 0 ]; then
    echo "[INIT]  tmpfs 挂载成功"
else
    echo "[INIT]  tmpfs 挂载失败"
fi

mount -t romfs /dev/rom0 /mnt/flash
if [ $? -eq 0 ]; then
    echo "[INIT]  romfs 挂载成功"
else
    echo "[INIT]  romfs 挂载失败"
fi

# 设置文件权限
echo "[INIT] 设置文件权限..."
chmod 755 /tmp
chmod 777 /tmp/logs

# 启动系统服务
echo "[INIT] 启动系统服务..."

# 启动 HTTP 服务器
if [ -x /bin/httpd ]; then
    httpd &
    echo "[INIT]  HTTP 服务器已启动"
fi

# 启动 Telnet 服务器
if [ -x /bin/telnetd ]; then
    telnetd &
    echo "[INIT]  Telnet 服务器已启动"
fi

# 启动日志守护进程
if [ -x /bin/logd ]; then
    logd &
    echo "[INIT]  日志守护进程已启动"
fi

echo "[INIT] ==================================="
echo "[INIT]  系统初始化完成!"
echo "[INIT] ==================================="

4.2 文件备份脚本

文件名backup.sh

功能:定期备份重要文件,支持自定义源目录和目标目录,自动生成带时间戳的备份文件。

#!/bin/sh
# 文件备份脚本
# 用法: backup.sh <源目录> <目标目录>

# 检查参数数量
if [ $# -lt 2 ]; then
    echo "用法: $0 <源目录> <目标目录>"
    echo "示例: $0 /tmp/data /mnt/flash/backup"
    exit 1
fi

SRC_DIR=$1
DST_DIR=$2

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

# 创建目标目录
mkdir -p $DST_DIR
if [ $? -ne 0 ]; then
    echo "错误: 无法创建目标目录 $DST_DIR"
    exit 1
fi

# 获取当前日期时间
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="backup_$DATE"

echo "=========================================="
echo "  文件备份脚本"
echo "=========================================="
echo "源目录:    $SRC_DIR"
echo "目标目录:  $DST_DIR"
echo "备份名称:  $BACKUP_NAME"
echo "开始时间:  $(date)"
echo "------------------------------------------"

# 执行备份
echo "正在执行备份..."
cp -r $SRC_DIR/* $DST_DIR/$BACKUP_NAME/

if [ $? -eq 0 ]; then
    echo "------------------------------------------"
    echo "备份成功!"
    echo "备份位置: $DST_DIR/$BACKUP_NAME"
    
    # 显示备份文件大小
    echo "备份大小: $(du -sh $DST_DIR/$BACKUP_NAME | awk '{print $1}')"
else
    echo "------------------------------------------"
    echo "备份失败!"
    exit 1
fi

echo "结束时间:  $(date)"
echo "=========================================="

执行示例

nsh> sh backup.sh /tmp/data /mnt/flash/backup
==========================================
  文件备份脚本
==========================================
源目录:    /tmp/data
目标目录:  /mnt/flash/backup
备份名称:  backup_20240101_120000
开始时间:  Tue Jan  1 12:00:00 2024
------------------------------------------
正在执行备份...
------------------------------------------
备份成功!
备份位置: /mnt/flash/backup/backup_20240101_120000
备份大小: 128K
结束时间:  Tue Jan  1 12:00:05 2024
==========================================

4.3 系统信息收集脚本

文件名sysinfo.sh

功能:收集并显示系统运行状态信息,包括内存使用、进程状态、文件系统、网络配置等。

#!/bin/sh
# 系统信息收集脚本

echo "=========================================="
echo "  NuttX 系统信息"
echo "=========================================="

# 系统基本信息
echo "\n【系统信息】"
echo "------------------------------------------"
echo "操作系统: $(uname -s)"
echo "内核版本: $(uname -r)"
echo "主机名称: $(uname -n)"
echo "运行时间: $(uptime)"

# CPU 信息
echo "\n【CPU 信息】"
echo "------------------------------------------"
if [ -f /proc/cpuinfo ]; then
    cat /proc/cpuinfo
else
    echo "CPU 信息不可用"
fi

# 内存信息
echo "\n【内存信息】"
echo "------------------------------------------"
free -h

# 进程信息
echo "\n【进程信息】"
echo "------------------------------------------"
echo "进程数量: $(ps | grep -v "PID" | wc -l)"
echo "\n详细列表:"
ps -v

# 文件系统信息
echo "\n【文件系统信息】"
echo "------------------------------------------"
mount

echo "\n磁盘使用情况:"
df -h

# 网络信息
echo "\n【网络信息】"
echo "------------------------------------------"
ifconfig

echo "\n路由表:"
route

echo "\n网络连接:"
netstat -a

# 环境变量
echo "\n【环境变量】"
echo "------------------------------------------"
set

echo "\n=========================================="
echo "  系统信息收集完成"
echo "=========================================="

4.4 日志管理脚本

文件名logmanage.sh

功能:管理系统日志文件,支持日志查看、清理、备份和压缩功能。

#!/bin/sh
# 日志管理脚本
# 用法: logmanage.sh <命令> [参数]
# 命令: view | clean | backup | compress

LOG_DIR="/tmp/logs"

# 显示帮助信息
show_help() {
    echo "日志管理脚本"
    echo "用法: $0 <命令> [参数]"
    echo ""
    echo "命令列表:"
    echo "  view    - 查看日志文件"
    echo "  clean   - 清理日志文件"
    echo "  backup  - 备份日志文件"
    echo "  compress - 压缩日志文件"
    echo "  help    - 显示帮助信息"
    echo ""
    echo "示例:"
    echo "  $0 view system.log"
    echo "  $0 clean"
    echo "  $0 backup"
    echo "  $0 compress"
}

# 查看日志
view_log() {
    if [ -z "$1" ]; then
        echo "用法: $0 view <日志文件名>"
        echo "可用日志文件:"
        ls $LOG_DIR/*.log 2>/dev/null || echo "  无日志文件"
        return
    fi
    
    LOG_FILE="$LOG_DIR/$1"
    if [ -f "$LOG_FILE" ]; then
        echo "查看日志: $LOG_FILE"
        echo "------------------------------------------"
        cat "$LOG_FILE"
    else
        echo "错误: 日志文件 $LOG_FILE 不存在"
    fi
}

# 清理日志
clean_log() {
    echo "清理日志文件..."
    echo "当前日志文件:"
    ls -la $LOG_DIR/*.log 2>/dev/null || echo "  无日志文件"
    
    read -p "确定要清空所有日志文件吗? (y/n): " CONFIRM
    if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then
        rm -f $LOG_DIR/*.log
        echo "日志文件已清理"
    else
        echo "取消清理"
    fi
}

# 备份日志
backup_log() {
    DATE=$(date +%Y%m%d_%H%M%S)
    BACKUP_FILE="$LOG_DIR/log_backup_$DATE.tar"
    
    echo "备份日志文件..."
    tar cf "$BACKUP_FILE" $LOG_DIR/*.log 2>/dev/null
    
    if [ $? -eq 0 ]; then
        echo "备份成功: $BACKUP_FILE"
        echo "备份大小: $(du -h "$BACKUP_FILE" | awk '{print $1}')"
    else
        echo "备份失败,无日志文件需要备份"
    fi
}

# 压缩日志
compress_log() {
    echo "压缩日志文件..."
    
    for LOG_FILE in $LOG_DIR/*.log; do
        if [ -f "$LOG_FILE" ]; then
            gzip "$LOG_FILE"
            echo "压缩: ${LOG_FILE}.gz"
        fi
    done
    
    echo "压缩完成"
}

# 主程序
if [ $# -lt 1 ]; then
    show_help
    exit 1
fi

case "$1" in
    view)
        view_log "$2"
        ;;
    clean)
        clean_log
        ;;
    backup)
        backup_log
        ;;
    compress)
        compress_log
        ;;
    help)
        show_help
        ;;
    *)
        echo "错误: 未知命令 '$1'"
        show_help
        exit 1
        ;;
esac

4.5 网络测试脚本

文件名nettest.sh

功能:测试网络连通性和带宽,支持 ping 测试、端口扫描、网络速度测试等功能。

#!/bin/sh
# 网络测试脚本

echo "=========================================="
echo "  NuttX 网络测试工具"
echo "=========================================="

# 显示网络接口信息
echo "\n【网络接口】"
echo "------------------------------------------"
ifconfig

# Ping 测试
echo "\n【Ping 测试】"
echo "------------------------------------------"

TARGETS="127.0.0.1 192.168.1.1 8.8.8.8"

for TARGET in $TARGETS; do
    echo "\n测试 $TARGET:"
    ping -c 3 $TARGET
    if [ $? -eq 0 ]; then
        echo "  ✓ 连通"
    else
        echo "  ✗ 不通"
    fi
done

# 网络连接状态
echo "\n【网络连接状态】"
echo "------------------------------------------"
netstat -a

# 路由表
echo "\n【路由表】"
echo "------------------------------------------"
route

echo "\n=========================================="
echo "  网络测试完成"
echo "=========================================="

4.6 文件系统检查脚本

文件名fscheck.sh

功能:检查文件系统健康状态,包括磁盘空间、文件数量、目录结构等。

#!/bin/sh
# 文件系统检查脚本

echo "=========================================="
echo "  文件系统检查工具"
echo "=========================================="

# 磁盘使用情况
echo "\n【磁盘使用情况】"
echo "------------------------------------------"
df -h

# 根目录大小统计
echo "\n【根目录大小统计】"
echo "------------------------------------------"
for DIR in /bin /etc /tmp /mnt; do
    if [ -d $DIR ]; then
        SIZE=$(du -sh $DIR 2>/dev/null | awk '{print $1}')
        COUNT=$(find $DIR -type f 2>/dev/null | wc -l)
        echo "$DIR: $SIZE, 文件数: $COUNT"
    fi
done

# 临时文件检查
echo "\n【临时文件检查】"
echo "------------------------------------------"
echo "/tmp 目录文件数量: $(find /tmp -type f | wc -l)"
echo "/tmp 目录大小: $(du -sh /tmp | awk '{print $1}')"

# 检查磁盘空间告警
echo "\n【磁盘空间告警】"
echo "------------------------------------------"
DISK_USAGE=$(df / | grep -v "Filesystem" | awk '{print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 80 ]; then
    echo "警告: 根目录使用率达到 ${DISK_USAGE}%,请清理空间"
else
    echo "正常: 根目录使用率 ${DISK_USAGE}%"
fi

# 文件系统挂载点检查
echo "\n【挂载点检查】"
echo "------------------------------------------"
mount | awk '{print $1 " -> " $3 " (" $5 ")"}'

echo "\n=========================================="
echo "  文件系统检查完成"
echo "=========================================="

五、脚本配置与启用

5.1 通过 menuconfig 配置

make menuconfig

配置路径

Application Configuration
  ---> NSH Library
    ---> [*] Enable built-in applications
      ---> [*] sh command           # 启用脚本支持
      ---> [*] source command       # 启用 source 命令
      ---> [*] Enable NSH debug output
    ---> [*] Enable command line history
    ---> [*] Enable environment variables
    ---> [*] Enable file descriptor redirection
    ---> [*] Enable pipes

5.2 关键配置选项

配置选项 说明 默认值
CONFIG_NSH_BUILTIN_APPS 启用内置应用 y
CONFIG_NSH_DISABLESCRIPT 禁用脚本支持 n
CONFIG_NSH_FILEIOSIZE 文件 I/O 缓冲区大小 512
CONFIG_NSH_ENVIRON 启用环境变量支持 y
CONFIG_NSH_REDIRECT 启用重定向支持 y
CONFIG_NSH_PIPE 启用管道支持 y
CONFIG_NSH_MAXARGUMENTS 最大命令行参数数量 16
CONFIG_NSH_HISTORY 命令历史记录大小 16

5.3 自动启动脚本

方式一:通过 rcS 文件

在文件系统根目录创建 etc/rcS 文件,系统启动时会自动执行:

# 创建 rcS 文件
nsh> mkdir -p /etc
nsh> cat > /etc/rcS << EOF
#!/bin/sh
echo "执行自动启动脚本..."
/mnt/flash/scripts/init.sh
EOF

方式二:通过 Kconfig 配置

make menuconfig
Application Configuration
  ---> NSH Library
    ---> [*] Execute rcS on startup
    ---> Path to rcS script (Default: /etc/rcS)

六、脚本开发最佳实践

6.1 脚本编写规范

1. 始终添加 Shebang

#!/bin/sh

2. 添加脚本头部注释

#!/bin/sh
# 脚本名称: script_name.sh
# 功能描述: 简要描述脚本功能
# 作者: Your Name
# 日期: YYYY-MM-DD
# 用法: script_name.sh [参数]

3. 检查命令返回值

cmd
if [ $? -eq 0 ]; then
    echo "成功"
else
    echo "失败"
    exit 1
fi

4. 使用绝对路径

# 推荐
/mnt/flash/scripts/init.sh

# 不推荐(依赖当前目录)
./init.sh

5. 添加错误处理

#!/bin/sh
set -e  # 遇到错误立即退出

# 或手动检查
if [ ! -f "$FILE" ]; then
    echo "错误: 文件不存在"
    exit 1
fi

6.2 性能优化建议

1. 减少命令调用次数

# 低效
for i in $(ls *.txt); do
    echo $i
done

# 高效
for i in *.txt; do
    echo $i
done

2. 使用临时文件

# 使用临时文件存储中间结果
TEMP_FILE=$(mktemp)
cmd > $TEMP_FILE
process $TEMP_FILE
rm $TEMP_FILE

3. 避免不必要的输出

# 关闭不必要的输出
cmd > /dev/null 2>&1

七、常见问题与解决方案

7.1 脚本命令未找到

问题

nsh> sh test.sh
sh: command not found

解决方案

在 menuconfig 中启用 sh 命令:

make menuconfig
# Application Configuration -> NSH Library -> sh command

7.2 脚本执行权限问题

问题

nsh> ./test.sh
nsh: ./test.sh: Permission denied

解决方案

# 添加可执行权限
nsh> chmod 755 test.sh

7.3 脚本语法错误

问题:脚本执行时报语法错误

解决方案

  1. 检查脚本中的命令是否在 NSH 中支持
  2. 避免使用 NSH 不支持的高级 Shell 语法(如 $(()) 算术扩展)
  3. 确保脚本使用正确的换行符(Unix 格式)

7.4 脚本无法读取环境变量

问题:脚本中无法读取在命令行设置的环境变量

解决方案

使用 source 命令执行脚本:

nsh> set MY_VAR=hello
nsh> source test.sh  # 可以读取 MY_VAR

7.5 文件系统未挂载

问题:脚本执行时提示文件不存在

解决方案

确保文件系统已正确挂载:

nsh> mount -t tmpfs none /tmp
nsh> mount -t romfs /dev/rom0 /mnt/flash

八、脚本执行流程图

8.1 脚本执行完整流程

文件系统 命令执行器 脚本解析器 NSH Shell 用户 文件系统 命令执行器 脚本解析器 NSH Shell 用户 sh script.sh 打开脚本文件 返回文件内容 解析脚本内容 逐行读取 变量替换 命令展开 执行命令 文件操作 返回结果 返回执行状态 继续下一行 脚本执行完成 返回退出码

8.2 脚本变量处理流程

读取脚本行

包含变量?

直接执行命令

解析变量名

变量是否存在?

变量替换为空

获取变量值

替换变量为值

执行替换后的命令

还有下一行?

脚本结束


九、结束语

NuttX 的脚本系统为嵌入式开发提供了强大的自动化能力。通过脚本文件,开发者可以实现:

  • 系统初始化:自动完成目录创建、环境配置、服务启动
  • 批量操作:一次性执行多个命令,提高效率
  • 系统管理:日志管理、文件备份、系统监控
  • 网络测试:连通性测试、性能测试

本文介绍了 NuttX 脚本系统的核心功能、常用脚本示例和开发最佳实践。希望这些内容能帮助您更好地利用脚本提升嵌入式开发效率。

下一步建议

  1. 在实际项目中尝试使用脚本自动化重复任务
  2. 根据项目需求编写自定义脚本
  3. 结合 NuttX 的其他功能(如定时器、信号)实现更复杂的自动化场景

参考资料


如果您觉得本文对您有帮助,请点赞、收藏并分享给更多朋友!如有任何问题或建议,欢迎在评论区留言讨论!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐