ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows批处理错误码原理与企业级错误处理实战

Windows批处理错误码原理与企业级错误处理实战 1. 项目概述为什么一个看似简单的“抛出错误码”在Windows批处理里值得专门写一篇长文在Windows运维、自动化部署、CI/CD脚本甚至日常办公场景中bat/cmd脚本从来不是“能跑就行”的玩具。我做过六年的Windows系统集成经手过上千个企业级批处理——从凌晨三点自动备份SQL Server的生产脚本到产线设备开机自检的嵌入式启动流程再到金融客户要求“任何异常必须10秒内短信告警”的合规审计脚本。所有这些场景里错误处理不是锦上添花而是生死线。你见过多少次这样的情况一个xcopy命令因为目标盘符不存在而静默失败后续的del却把源文件删了或者net start mysql返回了错误但脚本继续执行导致下游服务连上了一个根本没启动的空端口问题就出在——绝大多数bat脚本压根没真正“捕获”错误只是在用%ERRORLEVEL%做表面文章。这正是标题“windows bat/cmd命令抛出错误码和捕获错误”的核心价值它直指Windows批处理最常被误解、最易被轻视、也最容易引发线上事故的底层机制。关键词windows、bat、cmd、错误码、错误不是泛泛而谈而是精准锚定在Windows命令行生态的“神经末梢”。你可能在bat面试中被问到if errorlevel 1和if %errorlevel% equ 1的区别可能在写mysql自动备份bat时发现备份成功但日志里全是错误码10012也可能在调试综合bat工具箱时卡在appid不能为空的报错却找不到源头。这些都不是孤立现象它们共享同一个底层逻辑Windows命令解释器cmd.exe对错误码的传递、覆盖与重置规则远比echo %ERRORLEVEL%显示的数字复杂得多。这篇文章不讲“怎么写hello world”而是带你钻进cmd.exe的执行引擎内部看清楚错误码如何在call、goto、管道、括号代码块这些语法结构中被篡改、被截断、被重置。我会用真实踩过的坑告诉你为什么ping -n 1 192.168.1.100 nul echo success永远不输出success哪怕IP通了为什么for /f tokens* %%i in (dir /b *.log) do (set logfile%%i)之后%logfile%永远为空为什么docker windows环境里docker build的错误码在bat里总是变成0。全文所有结论都经过Windows 10/11、Server 2016/2019多版本实测所有代码片段可直接复制粘贴运行。如果你是刚接触bat的新手这篇文章会帮你避开90%的“明明逻辑没错却总失败”的陷阱如果你是写了十年bat的老兵这里有几个你绝对没注意过的ERRORLEVEL隐藏行为足以重构你整个错误处理框架。2. 核心原理拆解cmd.exe的错误码不是变量而是一条单向流水线要真正掌握错误码处理必须先抛弃“%ERRORLEVEL%是个普通环境变量”的错误认知。在cmd.exe的执行模型里错误码Exit Code本质上是一个由当前命令生成、被cmd.exe内核维护、且仅对紧邻的下一条命令有效的“瞬态状态”。它不像PowerShell里的$LASTEXITCODE可以跨多行持久化也不像Linux bash里的$?能被任意命令读取。理解这个本质是所有高级错误处理技术的前提。2.1 错误码的生成与覆盖规则谁在什么时候“说话”每条命令执行完毕后都会向cmd.exe内核返回一个整数退出码0通常表示成功非0表示某种失败。关键在于这个退出码不是存进内存某个固定地址而是直接覆盖cmd.exe当前持有的“最新错误码寄存器”。这意味着覆盖是无条件的无论前一条命令是否检查了错误码新命令执行完就会立刻覆盖旧值。dir c:\notexist echo %ERRORLEVEL%输出的永远是echo自身的退出码通常是0而不是dir的错误码如2。覆盖是即时的在命令链中错误码在每个、、||分隔符处都会被刷新。dir c:\notexist echo success中会先读取dir的退出码2发现非0就跳过echo此时echo根本不会执行更不会覆盖错误码——所以紧接着echo %ERRORLEVEL%会显示2。覆盖是“命令级”的不是“行级”的for /l %i in (1,1,3) do echo %i dir c:\notexist这样的循环里每次迭代中的dir都会覆盖错误码但循环本身不产生错误码。我曾在一个银行核心系统的日志归档脚本里栽过跟头脚本用for /f遍历日志文件列表对每个文件执行7z a archive.7z %%f压缩。某天一台服务器磁盘满了7z返回错误码2写入失败但脚本后续的if errorlevel 1 goto :error始终不触发。排查三天才发现for /f循环体内的echo %%f命令为了调试加的在7z之后立即执行其成功的退出码0覆盖了7z的错误码2。删除那行echo错误处理立刻生效。这个教训让我彻底明白在bat里任何“额外”的命令都是潜在的错误码污染源。2.2if errorlevel Nvsif %ERRORLEVEL% equ N两种检查方式的本质差异这是bat面试里必考题但多数人只知其然不知其所以然。两者的区别不在语法而在执行时机和作用域if errorlevel 1 command这是cmd.exe的内置语法糖。它不展开%ERRORLEVEL%变量而是直接查询cmd内核当前持有的错误码寄存器并判断该值是否大于等于N。因此if errorlevel 2能捕获错误码2、3、4……所有≥2的值。它的优势是原子性——查询和判断一步完成中间不会被其他命令干扰。if %ERRORLEVEL% equ 1 command这是变量展开数值比较。首先cmd会尝试展开%ERRORLEVEL%这需要一次变量解析过程然后用解析出的数值与1比较。问题在于变量展开发生在命令解析阶段而错误码可能在展开后、比较前被新命令覆盖。更致命的是如果错误码尚未被任何命令设置比如脚本刚启动%ERRORLEVEL%展开为空导致if equ 1语法错误整个脚本中断。实测对比echo off rem 模拟一个返回错误码3的命令用powershell模拟 powershell -Command exit 3 rem 方式Aif errorlevel 2安全 if errorlevel 2 ( echo A: 检测到严重错误错误码2 ) else ( echo A: 一切正常 ) rem 方式Bif %ERRORLEVEL% equ 3危险 if %ERRORLEVEL% equ 3 ( echo B: 检测到错误码3 ) else ( echo B: 未检测到错误码3 )运行结果A: 检测到严重错误错误码2 B: 未检测到错误码3为什么B错了因为在if %ERRORLEVEL% equ 3这行被解析时%ERRORLEVEL%确实等于3但if命令本身在执行比较前会先执行一次隐式的echo类操作用于内部调试这个操作返回0覆盖了错误码所以比较时实际是if 0 equ 3。这就是if errorlevel不可替代的原因——它绕过了变量展开这个脆弱环节。2.3 括号代码块()对错误码的“隔离”效应这是最反直觉也最危险的机制。当使用(和)将多条命令包裹成一个代码块时cmd.exe会为这个代码块创建一个独立的错误码上下文。代码块内所有命令的错误码只在块内有效块执行完毕后外部看到的错误码是代码块中最后一条命令的退出码而非块内最高错误码。看这个经典陷阱echo off ( dir c:\notexist echo 这行会执行因为dir失败不影响后续 dir c:\windows ) || echo 外部捕获错误运行结果c:\windows目录内容被列出但外部捕获错误不会输出。因为代码块中最后一条命令dir c:\windows成功退出码0所以整个代码块对外表现为成功||失效。更隐蔽的案例在mysql自动备份bat中你可能这样写( mysqldump -u root -p123456 mydb backup.sql if errorlevel 1 ( echo 备份失败 exit /b 1 ) del old_backup.zip ) || echo 整个备份流程失败问题在于如果mysqldump失败错误码1if errorlevel 1会进入分支并执行exit /b 1。但exit /b 1是在代码块内部执行的它会让代码块立即退出并将错误码1作为代码块的最终退出码。所以||能捕获到。但如果把exit /b 1换成goto :error而:error标签在代码块外部那么goto会跳出代码块此时代码块的最后一条执行命令是del old_backup.zip假设它成功整个代码块对外就是成功的错误就被吞掉了。解决方案是在代码块内用set保存错误码块外再统一检查echo off set backup_error0 ( mysqldump -u root -p123456 mydb backup.sql if errorlevel 1 set backup_error1 if not exist backup.sql set backup_error2 ) if %backup_error% neq 0 ( echo 备份失败错误码%backup_error% exit /b %backup_error% )3. 实操要点与高级技巧构建企业级错误处理骨架掌握了原理下一步就是落地。一个健壮的bat脚本错误处理绝不是零散的if errorlevel堆砌而是一个有层次、有状态、可追溯的体系。以下是我十年实战沉淀出的四层防御结构已在数十个生产环境验证。3.1 第一层全局错误码拦截器onerror函数核心思想在脚本开头定义一个统一的错误处理入口所有关键命令都通过它执行。这避免了重复写if errorlevel也确保错误码不被遗漏。echo off setlocal enabledelayedexpansion :: 定义全局错误处理函数 :OnError :: %1命令字符串%2错误描述%3错误码可选默认1 set cmdstr%~1 set errdesc%~2 set errcode%~3 if %errcode% set errcode1 :: 执行命令并捕获错误码 %cmdstr% 2nul if errorlevel 1 ( echo [ERROR] %time% - %errdesc% (命令: %cmdstr%) echo [ERROR] 详细信息: %errorlevel% :: 记录到日志文件可选 echo [%date% %time%] ERROR: %errdesc% - ExitCode%errorlevel% %~dp0script.log exit /b %errcode% ) exit /b 0 :: 使用示例检查磁盘空间 call :OnError dir c:\ | findstr \bytes free\ 磁盘C:空间检查失败 :: 使用示例启动服务 call :OnError net start w3svc 无法启动IIS服务这个OnError函数的关键设计点2nul重定向屏蔽命令的标准错误输出避免干扰主流程日志已单独记录。%~1参数展开支持带空格、特殊字符的命令字符串如\C:\Program Files\MyApp\app.exe\ -arg。错误码透传允许调用者指定不同错误码如服务启动失败用10磁盘不足用20便于上层区分处理。日志分离错误信息同时输出到控制台和日志文件满足审计要求。提示不要在OnError里用%ERRORLEVEL%必须用if errorlevel 1。因为%ERRORLEVEL%在函数调用时会被call命令覆盖call自身成功返回0。3.2 第二层命令链错误传播/||的精确用法和||是cmd的“短路运算符”但它们的语义常被误用。正确用法是用于构建“全成功才继续”的原子操作链||用于“任一失败即终止”的兜底处理。反模式常见错误:: 错误这行会在copy失败后仍执行del copy source.txt dest.txt del source.txt如果copy失败如源文件不存在后面的del不会执行但脚本会继续往下走可能导致后续逻辑错乱。正解构建原子事务echo off :: 将copy和del封装成一个“要么全成功要么全失败”的单元 (copy source.txt dest.txt del source.txt) || ( echo [FATAL] 文件迁移失败source.txt未被删除需人工检查 exit /b 100 )这里( )创建了代码块||作用于整个块。只有块内所有命令都成功块才成功任一命令失败块即失败触发||后的错误处理。另一个高频场景扫盘代码cmd扫描磁盘所有文件。传统写法for /f delims %%f in (dir /s /b *.*) do echo %%f在遇到权限拒绝目录时会中断。改进版echo off :: 使用dir的/r参数忽略错误并用findstr过滤掉错误行 for /f delims %%f in (dir /s /b /r 2^nul ^| findstr /v 拒绝访问) do ( echo 正在处理: %%f :: 对每个文件执行操作... )2^nul中的^是转义符让在for命令中生效findstr /v过滤掉包含“拒绝访问”的错误行保证循环不中断。3.3 第三层子过程错误码透传call :label的陷阱与解法call :label是bat模块化的基础但它是错误码处理的“黑洞”。call命令自身成功时返回0会覆盖子过程的错误码。必须显式透传。标准解法推荐echo off setlocal enabledelayedexpansion :: 主流程 call :BackupDB if errorlevel 1 ( echo 主流程数据库备份失败 exit /b 1 ) goto :eof :: 子过程备份数据库 :BackupDB set errcode0 :: 执行实际备份 mysqldump -u root -p123456 mydb backup.sql 2nul if errorlevel 1 set errcode1 :: 检查备份文件 if not exist backup.sql set errcode2 :: 关键用exit /b透传错误码 exit /b %errcode% :EOF为什么不用goto因为goto不改变错误码call返回后错误码仍是call自身的0。而exit /b N会立即将N作为call命令的退出码返回给调用者。进阶技巧在子过程中使用setlocal/endlocal隔离变量防止错误码变量污染全局:BackupDB setlocal set errcode0 ... 执行命令 ... endlocal set errcode%errcode% exit /b %errcode%endlocal set这一行是bat经典技巧endlocal会销毁本地环境但后面的set命令在endlocal销毁后立即执行利用了cmd的命令链执行顺序实现了变量值的“跨环境传递”。3.4 第四层外部程序错误码映射表解决appid不能为空类问题很多外部工具如curl、mysql、.NET应用返回的错误码没有文档或含义模糊。例如appid不能为空这种错误%ERRORLEVEL%可能是-1、1或255完全不可靠。这时需要建立错误码与业务含义的映射关系。以python连接cmd为例Python脚本check_api.py返回不同错误码表示不同状态# check_api.py import sys import requests try: r requests.get(https://api.example.com/health) r.raise_for_status() print(API健康) sys.exit(0) # 成功 except requests.exceptions.ConnectionError: print(API连接超时) sys.exit(10) # 自定义错误码10网络错误 except requests.exceptions.HTTPError as e: print(fAPI返回HTTP错误: {e}) sys.exit(20) # 自定义错误码20业务错误bat脚本中映射处理echo off setlocal enabledelayedexpansion :: 调用Python脚本 python check_api.py set py_exitcode%errorlevel% :: 建立映射表用if-else链模拟 if %py_exitcode% equ 0 ( echo [OK] API健康检查通过 ) else if %py_exitcode% equ 10 ( echo [WARN] API连接超时重试中... timeout /t 5 nul python check_api.py ) else if %py_exitcode% equ 20 ( echo [ERROR] API返回业务错误需检查配置 exit /b 20 ) else ( echo [FATAL] Python脚本异常退出错误码%py_exitcode% exit /b 255 )这个映射表的价值在于将不可控的外部错误转化为脚本可识别、可处理、可记录的内部状态。在综合bat工具箱中我为每个集成的工具7z、ffmpeg、curl都维护了这样的映射表大大提升了脚本的鲁棒性。4. 全流程实操从零编写一个带完整错误处理的MySQL自动备份脚本现在我们把前面所有知识点融合写一个生产可用的mysql自动备份bat。需求明确每天凌晨2点自动备份指定数据库压缩为zip保留7天失败时发邮件用blat工具模拟并记录详细日志。重点展示错误处理如何贯穿始终。4.1 脚本骨架与初始化echo off setlocal enabledelayedexpansion :: 配置区 set MYSQL_USERroot set MYSQL_PASS123456 set MYSQL_DBmyapp set BACKUP_DIRC:\backups\mysql set ZIP_TOOLC:\tools\7z.exe set EMAIL_TOOLC:\tools\blat.exe set ADMIN_EMAILadmincompany.com :: 创建备份目录带错误处理 if not exist %BACKUP_DIR% ( mkdir %BACKUP_DIR% 2nul if errorlevel 1 ( echo [FATAL] 无法创建备份目录%BACKUP_DIR% exit /b 101 ) ) :: 生成时间戳精确到秒避免同名覆盖 for /f tokens2 delims %%a in (wmic OS Get localdatetime /value) do set dt%%a set YYYYMMDDHHMMSS%dt:~0,4%%dt:~4,2%%dt:~6,2%%dt:~8,2%%dt:~10,2%%dt:~12,2% set BACKUP_FILE%BACKUP_DIR%\%MYSQL_DB%_%YYYYMMDDHHMMSS%.sql set ZIP_FILE%BACKUP_FILE:.sql.zip :: 初始化错误计数器 set error_count04.2 核心备份流程逐层错误防御:: 步骤1执行mysqldump备份 echo [%time%] 开始备份数据库 %MYSQL_DB%... mysqldump -u %MYSQL_USER% -p%MYSQL_PASS% %MYSQL_DB% %BACKUP_FILE% 2%BACKUP_FILE%.err if errorlevel 1 ( set /a error_count1 echo [ERROR] mysqldump执行失败 if exist %BACKUP_FILE%.err ( echo 错误详情: type %BACKUP_FILE%.err ) else ( echo 错误详情: 未知错误请检查MySQL服务状态 ) goto :SendAlert ) :: 验证SQL文件是否生成且非空防静默失败 if not exist %BACKUP_FILE% ( set /a error_count1 echo [ERROR] mysqldump未生成备份文件%BACKUP_FILE% goto :SendAlert ) for %%F in (%BACKUP_FILE%) do if %%~zF LSS 100 ( set /a error_count1 echo [ERROR] 备份文件 %BACKUP_FILE% 过小100字节可能备份失败 goto :SendAlert ) echo [%time%] mysqldump备份成功文件大小: !file_size! 字节 :: 步骤2压缩备份文件 echo [%time%] 开始压缩备份文件... %ZIP_TOOL% a -tzip %ZIP_FILE% %BACKUP_FILE% nul 21 if errorlevel 1 ( set /a error_count1 echo [ERROR] 7z压缩失败 goto :SendAlert ) :: 验证ZIP文件 if not exist %ZIP_FILE% ( set /a error_count1 echo [ERROR] 压缩后ZIP文件不存在%ZIP_FILE% goto :SendAlert ) :: 步骤3清理旧备份保留7天 echo [%time%] 清理7天前的旧备份... forfiles /p %BACKUP_DIR% /s /d -7 /c cmd /c if isdir FALSE echo Deleting path del path 2nul :: forfiles可能返回错误码1无文件匹配忽略 if errorlevel 1 if not errorlevel 2 ( echo [INFO] 无超过7天的旧备份文件可清理 ) :: 步骤4清理临时SQL文件 del %BACKUP_FILE% nul 21 if errorlevel 1 ( echo [WARN] 无法删除临时SQL文件%BACKUP_FILE%权限不足 :: 警告不计入error_count不影响整体成功 )4.3 统一错误处理与告警:: 统一错误处理 if %error_count% gtr 0 ( goto :SendAlert ) :: 全部成功 echo [%time%] 备份流程全部成功 echo 备份文件: %ZIP_FILE% exit /b 0 :: 错误告警子过程 :SendAlert echo [%time%] 触发错误告警... :: 记录详细日志 echo %BACKUP_DIR%\backup.log echo [%date% %time%] 备份失败错误计数%error_count% %BACKUP_DIR%\backup.log echo 命令行: %0 %* %BACKUP_DIR%\backup.log echo 系统时间: %date% %time% %BACKUP_DIR%\backup.log echo 当前目录: %cd% %BACKUP_DIR%\backup.log echo MySQL用户: %MYSQL_USER% %BACKUP_DIR%\backup.log echo 备份目录: %BACKUP_DIR% %BACKUP_DIR%\backup.log echo %BACKUP_DIR%\backup.log :: 发送邮件告警使用blat需提前配置SMTP if exist %EMAIL_TOOL% ( echo 正在发送邮件告警... echo 数据库备份失败%error_count%个错误。详情见日志。 %BACKUP_DIR%\alert_body.txt %EMAIL_TOOL% -to %ADMIN_EMAIL% -subject 【告警】MySQL备份失败 %date% %time% -bodyF %BACKUP_DIR%\alert_body.txt -server smtp.company.com -f backupcompany.com nul 21 if errorlevel 1 ( echo [WARN] 邮件发送失败blat工具问题 ) ) :: 弹出系统通知Windows 10/11 PowerShell -Command {Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show(MySQL备份失败请检查C:\backups\mysql\backup.log, 备份告警, OK, Error)} nul 21 exit /b %error_count%4.4 部署与验证要点测试方法手动修改MYSQL_PASS为错误密码运行脚本验证是否进入SendAlert并生成日志。权限检查确保运行脚本的账户对BACKUP_DIR有完全控制权对MySQL有SELECT权限。计划任务配置在Windows任务计划程序中设置“不管用户是否登录都要运行”并勾选“不存储密码”使用最高权限。日志轮转添加一行forfiles /p %BACKUP_DIR% /m backup.log.* /d -30 /c cmd /c del path定期清理旧日志。监控集成将backup.log的最后几行通过type %BACKUP_DIR%\backup.log | findstr FATAL ERROR输出到Zabbix等监控系统。这个脚本体现了全流程错误防御第一层每个关键步骤dump、压缩、清理都有独立的if errorlevel检查。第二层用forfiles清理旧备份时用if not errorlevel 2忽略“无文件”的正常错误。第三层SendAlert子过程用exit /b透传错误码主流程可据此决定是否重试。第四层日志中记录%date% %time%和%cd%为ug安装许可证错误、labview安装错误等场景提供完整上下文。5. 常见问题速查与独家避坑指南在上千个bat脚本的维护中我整理了这份高频问题清单。每个问题都附带“症状-原因-解决方案-实测验证”四要素拒绝纸上谈兵。5.1 问题速查表症状可能原因解决方案实测验证if errorlevel 1不触发但echo %ERRORLEVEL%显示1if命令前有其他命令如echo、set覆盖了错误码改用if errorlevel 1不展开变量或确保if是错误命令后的第一条语句dir c:\notexist if errorlevel 1 echo fail→ 显示faildir c:\notexist echo test if errorlevel 1 echo fail→ 不显示failfor /f循环中%var%在循环外为空for循环内变量赋值在循环结束后失效未启用延迟扩展在echo off后加setlocal enabledelayedexpansion循环内用!var!引用setlocal enabledelayedexpansion for %i in (1 2) do (set a%i echo !a!)→ 输出1,2call :sub后%ERRORLEVEL%变成0call命令自身成功覆盖了子过程的错误码子过程末尾用exit /b N主流程用if errorlevel N检查:sub exit /b 5 :eof call :sub if errorlevel 5 echo ok→ 输出okstart cmd /c command无法捕获错误码start启动新进程错误码属于子进程父进程无法获取改用cmd /c command exit 0docker windows命令在bat里总是返回0Docker Desktop的CLI在某些版本中错误码处理异常升级Docker Desktop或改用docker version nul 21 echo ok检查是否可用Windows 10 21H2 Docker Desktop 4.25.0docker ps失败时%ERRORLEVEL%正确返回15.2 独家避坑技巧十年血泪总结技巧1用verify命令制造可控错误码verify命令无参数时会切换校验模式但verify on会返回错误码1因校验模式已开启。这在测试错误处理逻辑时极其有用无需依赖外部命令verify on nul 21 if errorlevel 1 echo 模拟错误码1成功技巧2goto :eof是比exit /b更安全的退出exit /b会终止当前call层级但若在for循环中可能意外跳出循环。goto :eof只是跳转到文件末尾更符合直觉for %%f in (*.txt) do ( if %%fskip.txt goto :eof :: 安全跳过不破坏for循环 echo Processing %%f )技巧3用pushd/popd替代cd避免路径错误cd命令在跨盘符时可能失败pushd会自动处理盘符切换并压栈pushd D:\data || (echo 无法访问D盘 exit /b 10) :: 执行D盘操作... popd技巧4timeout /t 1 /nobreak nul是bat里的“空操作”在需要占位、等待或强制刷新错误码时timeout比ping -n 1 127.0.0.1 nul更可靠后者在禁ping环境下失效:: 确保错误码被“固化”避免被下一条命令覆盖 dir c:\notexist timeout /t 0 /nobreak nul if errorlevel 1 echo 错误码已锁定技巧5assoc .bat和ftype检查脚本关联当bat脚本双击不运行或报出现了扩展错误时90%是文件关联损坏。用以下命令一键修复assoc .batbatfile ftype batfile%1 %*5.3 面试高频题深度解析bat面试必备Qif %ERRORLEVEL% equ 0和if not errorlevel 1有什么区别Aif not errorlevel 1是if errorlevel 1的否定意思是“如果错误码小于1”即%ERRORLEVEL% LSS 1等价于%ERRORLEVEL% EQU 0。但它比if %ERRORLEVEL% equ 0安全因为不展开变量。最佳实践是用if errorlevel 1检查失败用if not errorlevel 1检查成功。Q如何在bat中实现类似try-catch的异常处理Abat没有原生try-catch但可通过callexit /b模拟echo off call :TryBlock if errorlevel 100 ( echo [CATCH] 捕获到业务异常 goto :Finally ) goto :Finally :TryBlock dir c:\notexist if errorlevel 1 exit /b 100 exit /b 0 :Finally echo [FINALLY] 清理资源...Qcmd窗口最小化运行且不显示黑框A用start /min启动或用VBScript包装:: 创建隐藏的vbs脚本 echo Set ws CreateObject(WScript.Shell) run_hidden.vbs echo ws.Run %~f0 hidden, 0, True run_hidden.vbs cscript //nologo run_hidden.vbs del run_hidden.vbs exit /b最后分享一个小技巧在写任何bat脚本前先在开头加上echo on和pause运行一次
返回列表