找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 3|回复: 0

脚本背包客/sdcard/auto自动备份

[复制链接]
发表于 11 分钟前 | 显示全部楼层 |阅读模式
脚本背包客/sdcard/auto自动备份

QQ截图20261110081705.png

QQ截图20261110081653.png

QQ截图20261110081641.png


  1. /**
  2. * 案例39:脚本背包客/sdcard/auto自动备份
  3. *
  4. * 题材来源:设备农场数据保命刚需。云手机随时可能被重置/回收,攒了几个月的
  5. *          脚本一夜归零;手动拷备份没人坚持。本案例把备份做成一条命令:
  6. *          打包 → 只留最新 N 份 → 备份前查磁盘别把盘塞爆,长期跑就是数据保险。
  7. *
  8. * 全走锤死通道(零新API):
  9. *   磁盘可用 → adb.exec("df /sdcard")            (案例32锤死)
  10. *   目录大小 → adb.exec("du -sk 目录")            (案例32锤死)
  11. *   打包     → zip -r 时间戳.zip 源目录,失败降级 tar -czf
  12. *   世代轮换 → ls -1 列表排序 + stat -c %s 算大小 + rm 删旧 (案例32锤死)
  13. *   建目录   → mkdir -p                            (案例35锤死)
  14. *   等待     → sleep.second(整秒)/ sleep.millisecond(毫秒),禁止裸 sleep()
  15. *
  16. * 写法合规:Rhino ES5,全 var、字符串拼接、函数先定义后调用、main() 末尾启动;
  17. * 节奏输出:关键动作前 3..2..1 倒计时,日志行间留白,方便盯屏录像。
  18. *
  19. * 用法:直接运行即备份一次。常驻玩法:外层套 while(true){ 备份一次(); sleep.second(3600); }
  20. *      还原:adb.exec("unzip -o 备份.zip -d /sdcard/") 或 tar -xzf。
  21. */

  22. // ========== 配置区 ==========
  23. var 备份源目录 = "/sdcard/auto";
  24. var 备份存放目录 = "/sdcard/auto_backup";
  25. var 最多保留份数 = 5;
  26. var 磁盘底线MB = 500;        // df 可用低于这个数就不许备份,防塞爆

  27. // ========== adb 通道(案例30锤死:JSON包{errorMsg,success}) ==========
  28. function adb跑(命令) {
  29.     try {
  30.         var 数据 = JSON.parse(adb.exec(命令, false));
  31.         var 成 = 数据.success == null ? "" : ("" + 数据.success);
  32.         var 错 = 数据.errorMsg == null ? "" : ("" + 数据.errorMsg).trim();
  33.         return { 成: 成, 错: 错 };
  34.     } catch (e) {
  35.         return { 成: "", 错: "exec异常 " + e };
  36.     }
  37. }

  38. // ========== 节奏倒计时(整秒一律 sleep.second) ==========
  39. function 节奏倒计时(秒数, 干什么) {
  40.     for (var i = 秒数; i > 0; i--) {
  41.         printl("⏰ " + i + " 秒后:" + 干什么);
  42.         sleep.second(1);
  43.     }
  44.     printl("🎬 " + 干什么 + " —— 开始");
  45.     sleep.millisecond(400);
  46. }

  47. // ========== 时间戳文件名 ==========
  48. function 补零(n) { return n < 10 ? "0" + n : "" + n; }

  49. function 时间戳() {
  50.     var d = new Date();
  51.     return "" + d.getFullYear() + 补零(d.getMonth() + 1) + 补零(d.getDate()) +
  52.            "_" + 补零(d.getHours()) + 补零(d.getMinutes()) + 补零(d.getSeconds());
  53. }

  54. // ========== df 查可用(案例32解析姿势) ==========
  55. function 查可用MB() {
  56.     var r = adb跑("df /sdcard");
  57.     var m = ("" + r.成).match(/(\d+)\s*\d+%\s+\/sdcard/);
  58.     if (m == null) {
  59.         // 兜底:倒数第二列一般是可用KB
  60.         var 行 = ("" + r.成).split("\n");
  61.         if (行.length >= 2) {
  62.             var 列 = ("" + 行[1]).trim().split(/\s+/);
  63.             if (列.length >= 4) {
  64.                 var kb = parseInt(列[3], 10);
  65.                 if (kb > 0) return Math.floor(kb / 1024);
  66.             }
  67.         }
  68.         return -1;
  69.     }
  70.     var 行2 = ("" + r.成).split("\n");
  71.     for (var i = 1; i < 行2.length; i++) {
  72.         var 列2 = ("" + 行2[i]).trim().split(/\s+/);
  73.         if (列2.length >= 4) {
  74.             var kb2 = parseInt(列2[3], 10);
  75.             if (kb2 > 0) return Math.floor(kb2 / 1024);
  76.         }
  77.     }
  78.     return -1;
  79. }

  80. // ========== du 查目录大小(KB) ==========
  81. function 查目录大小KB(目录) {
  82.     var r = adb跑("du -sk " + 目录);
  83.     var m = ("" + r.成).match(/(\d+)/);
  84.     return m == null ? -1 : parseInt(m[1], 10);
  85. }

  86. // ========== 打包:zip 优先,tar 兜底(各自用对的扩展名,路径由本函数返回) ==========
  87. function 打包(戳) {
  88.     var zip名 = 备份存放目录 + "/auto备份_" + 戳 + ".zip";
  89.     var r1 = adb跑("zip -r -q " + zip名 + " " + 备份源目录);
  90.     var s1 = 文件存在(zip名);
  91.     if (r1.错 === "" && s1 > 0) return { 法: "zip", 成: true, 路径: zip名 };
  92.     printl("   ⚠ zip 不通(" + r1.错.slice(0, 50) + "),降级试 tar");
  93.     sleep.millisecond(300);
  94.     var tar名 = 备份存放目录 + "/auto备份_" + 戳 + ".tar.gz";
  95.     var r2 = adb跑("tar -czf " + tar名 + " -C /sdcard auto");
  96.     var s2 = 文件存在(tar名);
  97.     if (r2.错 === "" && s2 > 0) return { 法: "tar", 成: true, 路径: tar名 };
  98.     return { 法: "无", 成: false, 路径: "", 错: r2.错.slice(0, 60) };
  99. }

  100. function 文件存在(路径) {
  101.     var r = adb跑("stat -c %s " + 路径);
  102.     var n = parseInt(("" + r.成).match(/(\d+)/) == null ? "-1" : ("" + r.成).match(/(\d+)/)[1], 10);
  103.     return n > 0 ? n : 0;
  104. }

  105. // ========== 世代轮换:按文件名时间戳排序,留最新N份 ==========
  106. function 轮换() {
  107.     var r = adb跑("ls -1 " + 备份存放目录);
  108.     var 列表 = ("" + r.成).split("\n");
  109.     var 备份们 = [];
  110.     for (var i = 0; i < 列表.length; i++) {
  111.         var 名 = ("" + 列表[i]).trim();
  112.         if (名.indexOf("auto备份_") === 0) 备份们.push(名);
  113.     }
  114.     备份们.sort();                       // 时间戳命名,字典序=时间序
  115.     var 删掉的MB = 0;
  116.     var 删掉数 = 0;
  117.     while (备份们.length > 最多保留份数) {
  118.         var 旧 = 备份们[0];
  119.         var 字节 = 文件存在(备份存放目录 + "/" + 旧);
  120.         adb跑("rm -f " + 备份存放目录 + "/" + 旧);
  121.         if (字节 > 0) 删掉的MB += Math.floor(字节 / 1048576);
  122.         删掉数++;
  123.         printl("   🗑 轮换删除旧备份: " + 旧);
  124.         sleep.millisecond(300);
  125.         备份们 = 备份们.slice(1);
  126.     }
  127.     return { 现有份数: 备份们.length, 清单: 备份们, 删掉数: 删掉数, 删掉的MB: 删掉的MB };
  128. }

  129. // ========== 主流程 ==========
  130. function main() {
  131.     printl("=== 案例39 脚本背包客 启动:备份 " + 备份源目录 + " → " + 备份存放目录 + " ===");
  132.     sleep.millisecond(500);

  133.     // ① 磁盘预检
  134.     节奏倒计时(3, "检查磁盘可用空间");
  135.     var 可用 = 查可用MB();
  136.     if (可用 < 0) {
  137.         printl("❌ df 没解析出可用空间,不敢备份,收工");
  138.         return;
  139.     }
  140.     printl("💾 磁盘可用: " + 可用 + " MB(底线 " + 磁盘底线MB + " MB)");
  141.     sleep.millisecond(500);
  142.     if (可用 < 磁盘底线MB) {
  143.         printl("❌ 可用低于底线,备份会塞爆磁盘 → 先跑案例32清道夫清盘,再来备份");
  144.         return;
  145.     }

  146.     // ② 源目录摸底
  147.     var 源KB = 查目录大小KB(备份源目录);
  148.     printl("📦 源目录大小: " + (源KB < 0 ? "未知" : Math.round(源KB / 1024) + " MB"));
  149.     sleep.millisecond(500);

  150.     // ③ 打包
  151.     节奏倒计时(3, "打时间戳压缩包");
  152.     adb跑("mkdir -p " + 备份存放目录);
  153.     var 戳 = 时间戳();
  154.     var 包 = 打包(戳);
  155.     if (!包.成) {
  156.         printl("❌ zip/tar 都不通:" + 包.错);
  157.         printl("💡 这台 ROM 没打包工具,备份通道到此为止");
  158.         return;
  159.     }
  160.     var 目标 = 包.路径;
  161.     var 字节 = 文件存在(目标);
  162.     printl("   ✅ 打包完成[" + 包.法 + "]: " + 目标);
  163.     printl("   📏 压缩包大小: " + Math.floor(字节 / 1048576) + " MB");
  164.     sleep.millisecond(500);

  165.     // ④ 世代轮换
  166.     节奏倒计时(3, "轮换旧备份(只留 " + 最多保留份数 + " 份)");
  167.     var 轮 = 轮换();

  168.     // ⑤ 战报
  169.     printl("========== 📋 背包战报 ==========");
  170.     printl("  本次备份: auto备份_" + 戳 + "(" + 包.法 + "," + Math.floor(字节 / 1048576) + " MB)");
  171.     printl("  现有世代: " + 轮.现有份数 + " 份" + (轮.删掉数 > 0 ? "(本轮删旧 " + 轮.删掉数 + " 份,释放约 " + 轮.删掉的MB + " MB)" : "(未触发轮换)"));
  172.     for (var i = 0; i < 轮.清单.length; i++) printl("    " + (i + 1) + ". " + 轮.清单[i]);
  173.     printl("💡 还原一条命令: adb.exec("unzip -o " + 目标 + " -d /sdcard/")" + (包.法 === "tar" ? "(tar 包用 tar -xzf)" : ""));
  174.     printl("   常驻: 外层套 while(true){ 全流程重跑; sleep.second(3600) } 每小时自动背包");
  175.     printl("=== 案例39 收工 ===");
  176. }

  177. // ========== 启动(Rhino ES5:函数全部定义完才调用) ==========
  178. main();
复制代码


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-11-11 07:01

安卓脚本软件 · 键鼠盒子 · HID · 机械臂

快速回复 返回顶部 返回列表

随机推荐 · 热帖 / 新帖 / 随便看看

社区会员(随机展示)