
简介面向Linux系统上实现BLE外围设备功能的开发资料包对应标签“linux ble periph”适合嵌入式或物联网开发者、以及需要让Linux设备通过Smart App接收BLE消息的工程师。内容围绕如何将Linux配置为蓝牙低功耗外设涵盖配置、编译、GATT服务等环节。压缩包含932个文件整体3.57MB以C语言源码和头文件为主422个c、257个h同时包含配置文件、Makefile、shell脚本及txt文档可支持从编译构建到运行调试的完整流程也兼顾了BlueZ协议栈常见组件的参考实现。目前已有386人学习下载。资料中可见大量测试示例和服务端/客户端用例例如GATT server/client、广播示例、蓝牙测试工具等可帮助理解BLE角色配置、服务广播与数据收发机制文档与说明文件则可用于核对编译依赖与系统配置。整体体量紧凑适合快速搭建Linux BLE外围设备开发环境并对照实践。1. files.tar.gz 到底是什么一个文件装下整个目录的约定接手一台旧服务器时最常看到的就是 files.tar.gz从网上下载源码包解压出来也常叫这个名字。它不是一个固定项目的产物而是一类归档包的默认约定先用 tar 把整个目录连同权限、属主、软链、硬链接装进一个数据流再用 gzip 把这段流压成 .gz于是就有了 files.tar.gz。这个文件解决三类问题备份目录、分发源码、迁移数据。适合后端开发、运维以及所有需要手动搬服务器文件的人。tar 和 gzip 的分工经常被混为一谈恰恰是很多解压翻车的根源。tar 负责格式化地摆好目录结构gzip 负责压小体积两者合并后读取时必须从头流式解到尾这决定了后面讲的很多边界行为。下面从打包、解压、增量备份、排错到复检按你实际操作时会遇到的顺序展开每段命令都能直接抄走但更重要的是知道参数为什么存在。2. 打包与压缩tar -czf 最小命令与常用参数拆解2.1 先分清 tar 和 gzip一个只管归档一个只管压体积tar 的名字来自 tape archive最初是磁带备份工具设计目标是把若干文件连同目录结构、权限、时间戳、属主、硬链接关系写进同一个连续数据流。它本身不压缩所以一个没压过的 tar 包经常比所有文件加起来还大一点因为要额外记录元数据。真正把体积压下来的是 gzip它把 tar 吐出来的连续流整体压缩。理解这个分工能解释很多现象。比如 tar -tzf 能列出包内文件但为什么不能像 zip 一样直接跳到某个文件因为 .gz 是整段流式压缩想读尾部必须从头解到尾部。再比如换压缩算法只需要替换字母-z 是 gzip-j 是 bzip2-J 是 xz归档的使用方式不变只是压缩率和速度不同。那为什么不直接用 zipzip 对每个文件单独压缩中心目录区记录索引随机读取很方便但 zip 在 unix 世界保存权限位、符号链接的能力参差不齐不同工具打出来的包行为经常是玄学。tar.gz 则完整保留 unix 属性代价是变成顺序流。所以源码发布包、容器镜像层、服务器备份绝大多数都是 tar.gz而不是 zip。2.2 tar -czf files.tar.gz ./data最小打包命令拆解最常见的打包命令就是这一条tar -czf files.tar.gz ./data拆开看-c 表示创建归档-z 表示用 gzip 压缩-f 后面必须紧跟归档文件名最后的 ./data 是你要打包的源路径。注意 -f 的位置它和文件名是一对必须写在组合参数里靠近末尾的位置写成 tar -czf 是惯例写 tar -fcz 也能跑但读起来容易看晕我习惯固定用 -czf。如果打包时想看到每个文件加一个 -vtar -czvf files.tar.gz ./data-v 会把正在归档的文件名逐个打印到终端文件多的时候输出非常吵。脚本里我一般不加 v手动打包时加一个确认进度。真正常用的是排除某些目录tar -czf files.tar.gz ./data \ --exclude./data/logs \ --exclude./data/.cache这段命令里 --exclude 后面跟的是包内路径不是磁盘绝对路径。所以打包 ./data 时排除项要写 ./data/logs而不是 /srv/app/data/logs。这个细节特别容易错从根目录打包和从上层目录打包同一个目录在包里的路径不一样排除规则也要跟着变。2.3 保留权限、硬链接与符号链接备份类归档的关键参数tar 比 zip 强的地方在于它默认就会记录权限位、属主、时间戳、符号链接和硬链接关系。但记录和恢复是两件事。创建归档时权限和时间戳一定被记录不需要额外参数符号链接默认只记录链接本身不打包链接指向的内容所以解压出来还能保持软链关系。如果你加了 -h--dereferencetar 会把符号链接指向的目标文件内容也打进去这会让包体积膨胀处理不好还会循环我一般不加。硬链接则不需要任何参数tar 自动识别同一 inode 只存一份数据解压时恢复成硬链接关系。解压时是否恢复权限取决于你以什么身份运行 tarroot 默认恢复归档里记录的权限和属主普通用户默认不恢复属主权限还要受当前 umask 影响。需要强制保持一致性时用下面这个组合tar -xzf files.tar.gz -C /opt/app --same-permissions如果反过来你想让解出来的文件归当前用户所有用 --no-same-owner。一个常见做法是从其他机器拷来的备份包解压时直接用 --no-same-owner避免把原机器的 uid 数字带过来。参数本身很简单但配合场景选对才不会被文件的属主和权限坑到。2.4 排除与白名单--exclude-from 与 --files-from 的组合打法备份一个大型项目时node_modules、.git、临时文件通常都要排除。规则多的时候命令行里写一串 --exclude 不清爽我会先把规则写进文件cat /tmp/backup.exclude EOF ./data/node_modules ./data/.git *.log EOF tar -czf files.tar.gz -X /tmp/backup.exclude ./data-X 是 --exclude-from 的缩写文件里每行一条匹配规则和命令行的 --exclude 规则一样都作用于归档内路径。反过来的场景是只想打包某几个文件比如只备份配置文件find /srv/app -name *.conf -type f -print /tmp/file.list tar -czf files.tar.gz -T /tmp/file.list-T 是 --files-from意思是从这个列表里读取要打包的文件。列表里可以是绝对路径tar 打包时会自动处理路径前缀。如果列表想通过管道传给 tar就用 -T -从 stdin 读。这个排错技巧值得记住不要直接在命令行手写几十个文件维护一个清单文件比反复改命令可靠得多。3. 解压 files.tar.gz 之前先回答三个问题路径、权限与选择性3.1 先 tar -tzf 看清单再 tar -xzf 动手拿到一个陌生的 files.tar.gz我做的第一件事永远是只看不解tar -tzf files.tar.gz | head -n 20 tar -tzf files.tar.gz | wc -l tar -tzf files.tar.gz | grep -E (^|/)\.git/第一条列出前 20 个成员快速看包内有没有顶层目录第二条统计成员总数判断包的大小和文件数量是否合理第三条检查是否夹带了不想解出来的内容。-t 表示列出不解包配合 -z 自动解压 gzip 流这一步不会修改当前目录十分安全。想看更详细的信息用 -tvzf每个文件的权限、属主、大小、修改时间都会显示和 ls -l 的输出格式很像。这里有个实际好处解压之前就知道包里有没有 setuid 程序、有没有奇怪的属主、有没有超大的文件给后续决定添加哪些解压参数提供依据。3.2 路径穿越与绝对路径为什么现代 tar 会报 Removing leadingtar 归档里存的是路径名字符串恶意或损坏的归档可以把路径写成 ../../tmp/evil或者 /etc/cron.d/evil。如果在不可信来源的包上直接解压就有路径穿越风险。GNU tar 默认做了两件事把归档里的绝对路径去掉前导 /遇到包含 .. 的路径则跳过该成员并打印警告。所以常见的 tar: Removing leading / from member names 不是错误恰恰是安全机制在工作。解压之前可以用一行 awk 主动检查包内路径tar -tzf files.tar.gz | awk /^\// || /(^|\/)\.\.\// {print}没有输出说明路径还算干净。真正解压时我更建议先解到临时目录确认内容再决定放哪mkdir -p /tmp/safe_extract tar -xzf files.tar.gz -C /tmp/safe_extract如果包里本来就带着顶层目录 ./data解出来会落在 /tmp/safe_extract/data。很多部署包的顶层目录对目标环境没有意义这时候用 --strip-components1 可以直接剥掉第一层。3.3 解压的四个常用参数-C、-k、--strip-components、--no-same-owner下面这张表覆盖了我日常解压 files.tar.gz 时用到的绝大部分参数参数作用典型场景-C DIR先切到 DIR 再解包固定解压到目标目录-k已存在的文件不覆盖避免旧包覆盖新文件--strip-componentsN剥掉前 N 层路径去掉包内顶层目录--no-same-owner解出的属主设为当前用户跨机器部署把它们组合起来就是部署现场最常见的命令tar -xzf files.tar.gz -C /opt/app --strip-components1 --no-same-owner逻辑是把包解开到 /opt/app去掉包内第一层目录同时避免 root 把归档里记录的 uid 恢复成原机器的数字。注意 -k 与 --overwrite 正好相反-k 跳过已存在文件--overwrite 强制覆盖。默认行为是有就覆盖且不提示这个默认值坑过不少人后面避坑章会说。3.4 选择性解压与按需取文件有时整个包不需要全解只要抽出某一个配置文件。tar 支持按包内路径选择性解压tar -xzf files.tar.gz ./data/app/config.yaml -C /tmp/config-check路径必须和包内路径完全一致所以先 tar -tzf 确认路径名否则会报 Not found in archive。抽出到 /tmp/config-check 后用 diff 和现网配置做对比再决定是否替换。批量抽某一类文件时配合通配符tar -xzf files.tar.gz --wildcards ./data/*.yaml -C /tmp/yaml-check这里的通配符必须用引号包起来否则 shell 会先展开把当前目录里的 .yaml 文件列表传给 tar结果完全不是你要的。--wildcards 让 tar 在包内路径上做模式匹配而不是精确匹配单个成员。4. 增量备份与流式处理把 tar.gz 从一次性归档变成生产线4.1 GNU tar 增量备份--listed-incremental 从 level 0 开始tar.gz 不只是打一次包它还常被用来做每日备份。GNU tar 提供了一套基于快照文件的增量机制核心是一个 .snar 快照文件记录扫描过的每个文件的 mtime、大小、inode。第一次做全量后续做增量tar --listed-incremental/var/backup/snap.snar --level0 -czf full_0.tar.gz ./data tar --listed-incremental/var/backup/snap.snar -czf inc_1.tar.gz ./data第一条命令里 --level0 明确指定这是全量备份同时生成快照文件第二条不带 --level由于快照已存在tar 默认从 level 1 开始做增量只打包在上一次之后发生变化的文件。增量包小很多数据目录一天只改几百兆的话inc_1.tar.gz 通常只有全量的零头。快照文件是这套机制的心脏。它必须和归档一起妥善保存丢了快照后续的增量包全部失效。而失效不是解压报错是静默地缺文件这一点在恢复时才暴露特别坑。4.2 恢复增量的正确顺序先基础包再按时间顺序叠加恢复增量备份不需要快照文件只需要归档本身但顺序必须严格mkdir -p /restore/data tar -xzf full_0.tar.gz -C /restore tar -xzf inc_1.tar.gz -C /restore先解全量基础包再按时间顺序解增量包。顺序反了增量会把基础包里的文件覆盖成旧状态或者报文件不存在结果完全不可用。恢复后建议用 diff -r 和源目录做一次对比验证不要只看文件数量。这里必须说清楚 tar 增量备份的边界它记录的是文件发生了什么变化不是数据库级别的连续日志也不擅长记录删除事件。如果源目录在两次备份之间删除了几个文件增量包不会记住这里少了一个文件恢复出来反而会多出这些文件。所以涉及重要数据时我会每周强制做一次 --level0 全量每天走增量不给删除操作留太多空隙。4.3 流式输出tar -cf - 接 pigz、gpg 与 splittar 真正厉害的地方在于流式处理。-f - 表示归档写到标准输出而不是文件。这样后面可以接任意程序先看两行示例tar -cf - ./data | pigz -4 files.tar.gz tar -cf - ./data | gpg -c --batch --passphrase backup-key -o files.tar.gz.gpg第一条里 -cf - 把未压缩的 tar 流喂给 pigzpigz 是 gzip 的多线程实现-4 是压缩级别多核机器能明显感受到速度提升第二条把 tar 流加密成 gpg 文件适合备份到不受信任的存储位置。tar 本身带的 -z 用的是单线程 gzip数据量大时 CPU 单核跑满换 pigz 是最划算的优化。流式打包还能配合 split 解决大包的搬运问题tar -czf - ./data | split -b 1G - files.tar.gz.part_ cat files.tar.gz.part_* | tar -xzf - -C /restoresplit 按 1GB 切块适合拷贝到 exFAT 这类有单文件大小限制的移动介质还原时 cat 把所有分片合并通过 stdin 喂给 tar。整个链路没有落地中间文件不用占双倍磁盘。4.4 从 stdin 解包管道另一端接什么怎么验证既然可以流式打包自然可以流式解包。管道场景里归档文件不一定是磁盘上的实体可能来自 cat、网络传输或加密层解密后的输出cat files.tar.gz | tar -xzf - -C /tmp/check这里 tar 的文件名参数是 -表示从 stdin 读取归档。要注意的是性能行为tar 从 stdin 读时无法预知归档总长度也无法像文件模式那样回跳。像 --delete 这类需要随机访问的操作在 stdin 模式下不可用。这也是很多人在管道里试图删包内文件失败的原因。管道另一端的输出可以先验证再解压避免把问题流解开tar -tzf - files.tar.gz | head -n 5用重定向而不是管道语义更清晰文件被打开后连接到 tar 的 stdin只列出前 5 个成员。看到内容符合预期再决定是否真正解包。这个习惯成本极低但能拦下大量来源不明的归档。5. tar 排错与避坑5 个让备份翻车的真实案例5.1 磁盘明明还有几个 G解压却报 No space left现象解压一个包含几十万小文件的源码包解到一半突然报 No space left on device但 df -h 看磁盘还有好几 GB。原因inode 耗尽了。每个文件都要占用一个 inode而 inode 总数在格式化时固定。几十万个小文件会瞬间吃光 inodedf -h 看的是块容量根本反映不出这个问题。解决先确认是不是 inode 问题df -i /restoreIUse% 接近 100% 就可以断定。清理无关的临时目录或者把大量小文件合并成少量大文件。如果这个目录是长期备份目标重新格式化时把 inode 密度调大比事后删文件靠谱得多。5.2 解压后文件属主变成数字或变成 root现象从 A 机器打包的 files.tar.gz到 B 机器解压ls -l 看到一堆 1000:1000有时直接变成 root:root。原因tar 记录的是数字 uid/gid不是用户名。跨机器时用户 id 不一致系统无法映射成名字就显示数字用 root 解压时tar 默认会把归档里的 uid 原样恢复于是变成别的机器的 root 或未知用户。解决部署场景统一加 --no-same-owner。需要固定属主时直接指定tar -xzf files.tar.gz -C /opt/app --no-same-owner --ownerapp --groupapp--owner 和 --group 在解压时强制覆盖归档里的属主信息。这个组合在容器镜像构建、应用发布目录里非常常用比解压后再 chown -R 少一步且更确定。5.3 同名文件被覆盖没有任何提示现象想用备份包补几个文件结果整个目录被旧包覆盖且终端没有任何提示。原因tar 解压的默认行为是已存在的同名文件直接覆盖不询问。这既是优点也是坑尤其在你不确定包里文件版本的情况下。解决用 -k--keep-old-files让已存在文件跳过tar -xzf files.tar.gz -C /opt/app --keep-old-files更稳妥的做法是先解到新目录diff 之后再同步mkdir -p /tmp/app_new tar -xzf files.tar.gz -C /tmp/app_new diff -r /opt/app /tmp/app_newdiff 输出就是后悔药哪些文件会被改动、哪些是多出来的一目了然。凡是往生产目录解压我都会先这么过一遍。5.4 增量备份恢复完文件对不上现象按顺序恢复了 full_0.tar.gz 和 inc_1.tar.gz结果某些新文件没有出现某些删掉的旧文件反而回来了。原因要么恢复顺序反了要么增量包对应的快照文件和归档不是同一套。快照文件一旦被另一次备份覆盖后续增量记录就会错乱而且恢复时不会报错只会静默地给错误结果。解决备份时把快照文件命名并归档在一起每次增量备份后复制一份快照到独立目录恢复前先确认归档时间顺序恢复目标使用空目录不混旧文件。不要依赖凭感觉判断哪个包是最新的文件名里带时间和层级是基本纪律。5.5 归档里的文件名带换行脚本 for 循环直接炸掉现象写了个 shell 脚本来统计包内文件数量结果统计出的文件数翻了两三倍日志里路径被拦腰截断。原因脚本写了 for f in $(tar -tzf files.tar.gz)shell 会按空格、换行、制表符分词包内文件名一旦包含特殊字符一个路径就被拆成多个变量。解决改用 while read 逐行处理tar -tzf files.tar.gz | while IFS read -r f; do echo $f doneIFS read -r 保证空格和反斜杠原样进入变量。另外有一个少有人知的参数 --verbatim-files-from当用 -T 从文件读取打包列表时tar 默认会解释列表里的反斜杠转义如果文件名本身含反斜杠或换行就会和预期不一致。加 --verbatim-files-from 后列表逐字照读不再做任何解释。6. 收尾技巧用 gzip -t 与 tar -df 给归档做复检6.1 五秒验证与差异比对归档做完、传完、解完都需要验证。我固定用三步gzip -t files.tar.gz tar -tzf files.tar.gz /tmp/list.txt tar -df files.tar.gz -C /restoregzip -t 只验证 gzip 压缩流完整不关心内部路径tar -tzf 验证归档结构可读tar -df 把归档里每个成员与 /restore 下的同名文件比较大小和 mtime有差异会直接打印。这三步加起来五秒左右能确认两件事包没损坏、解开后的内容和包内记录一致。实际部署时我会把这些步骤写成一个最小脚本#!/usr/bin/env bash set -euo pipefail ARCHIVE${1:-files.tar.gz} DEST${2:-/restore} gzip -t $ARCHIVE tar -tzf $ARCHIVE /dev/null tar -xzf $ARCHIVE -C $DEST --no-same-owner tar -df $ARCHIVE -C $DEST echo OK: $ARCHIVEset -euo pipefail 保证任一步失败就退出不会带着坏归档继续往下跑。--no-same-owner 避免把归档里的 uid 数字带到目标机器如果目标环境需要保持原属主去掉这个参数即可。我的个人习惯是任何 files.tar.gz 到我手上先 tar -tzf 看第一屏内容再决定后续参数。这个习惯帮我躲过好几次解错目录、覆盖生产配置的翻车。tar 本身不是一个智能工具但因为简单可靠反而能在服务器上活几十年把上面的检查动作养成习惯它就不会成为那个让你半夜爬起来恢复数据的黑匣子。希望帮到你。本文还有配套的精品资源点击获取