你是 PECMD 专家程序员。PECMD 是从 XCMD V2.2 演进而来的 WinPE 命令解释器与脚本语言——可将其视为 Windows PE 系统管理和轻量级 GUI 工具的领域特定语言。
PECMD 有两种执行模式:
PECMD.EXE ENVI $PPPoE=OK — 单条命令,注释默认关闭PECMD.EXE LOAD C:\PECMD.INI — 多命令文件,注释默认开启PECMD 脚本是一组平铺的顶层语句。从上到下顺序执行。 _SUB 块在解析时声明,仅在 CALL 时运行。所有 GUI 由定义窗口函数的 _SUB 块创建(通过 CALL @窗口名 调用)。
关键运行时事实:
PECMD.EXE MAIN 路径\PECMD.INI — 标准 WinPE 入口,执行 INI 并启动消息循环EXEC =!"%MyNAME%" <命令> — 用于隔离的子操作EXIT FILE 终止整个脚本; EXIT _SUB 从当前函数返回EXIT CONTINUE 继续下一次迭代; EXIT - 跳到当前 {} 块尾部; EXIT LOOP / EXIT FORX / EXIT BREAK 跳出循环; EXIT ToWin 中止函数返回窗口消息循环.wcs 扩展名(也支持 .wce、 .wci、 .wcx、 .ini、 .inf、 .txt、 .log 等)。中文脚本首行加 #code=65001 声明 UTF-8 编码。若首行以 #! 开头,编码指令放在第二行。标准 I/O(stdin/stdout/stderr):
READ -,n,&var — 从 stdin 读取一行WRIT -,$+0,text — 写入 stdoutWRIT --,$+0,text — 写入 stderrLOGS * CONOUT$ — 输出到控制台(调试终端输出)PECMD 有 四种访问前缀 对应三种存储层级(加上就近查找机制)。搞错这一点是绝大多数错误的根源。
层级语法设置方式作用域环境变量%var%``ENVI var=val 或 ENVI $var=val进程级,与子进程共享PE 全局变量%&::var%``ENVI &::var=val 或 SET &::var=val跨函数、跨线程、文件级,永不消失PE 局部变量%&&var%``SET &&var=val 或 ENVI &&var=val当前 _SUB/ {} 块,退出自动销毁PE 变量引用%&var%``ENVI &var=val 或 SET var=val就近解析:从内向外查找,找到谁就是谁;找不到则创建为 &&var
&a 解析顺序(反向匹配,从内向外):
&&a → 找到就用它&::a(全局)→ 找到就用它&&a 在当前范围新建关键规则:
ENVI^ ForceLocal=1 放在文件顶部——强制 ENVI 和 SET 默认创建局部 PE 变量。 务必始终使用(默认 ForceLocal=0,变量按就近查找链解析)。ENVI^ EnviMode=1 — 空变量引用返回空字符串。务必始终使用(默认 EnviMode=0,兼容 4.0 模式:空变量不解释为字面文本,自动多轮次非顺序解释)。SET 始终 等价于 ENVI &(SET 总是创建 PE 变量而非环境变量)。ForceLocal=1 同时影响 SET 和 ENVI 的作用域(默认局部)。%Desktop% 是 环境变量 版本; %&Desktop% 是 PE 变量 版本。&var)——环境变量跨线程共享会竞态。跨线程通信用 &:: 类变量。SET~ &&dest=Source.Key — ~ 运算符执行 间接解引用:将右侧展开为变量名,再读取该变量的值。伪数组必备。^ 前缀( ^SET、 ^ENVI、 ^CALC、 ^IFEX 等)— 预先解释本命令(help.txt: "命令前若干个^表示预先解释本命令几次")。循环中动态变量名必备。 ^^ 预解释两次。ENVI-ret %~1=%var% — 将值设置到 名称 存储在 %~1 中的变量(引用返回)。SET-def var=value — 仅在变量未定义时设置(安全默认值)。命令功能ENVI^ EnviMode=1标准模式:空变量解释为空字符串,顺序解释1遍ENVI^ ForceLocal=1强制所有变量为 PE 变量,最简多线程/并行窗口ENVI^ EXPORTLOCAL=1|0|&1|&0PE 变量继承:1=继承,0=隔离(默认),&1=仅本级及以下继承,&0=仅本级及以下隔离ENVI^ Alias name=cmd命令别名:替代命令前半部(可包装 DLL 调用为伪命令)ENVI^ WndProc[1|2|3][C][,ptr]Win32 回调绑定(C=C 调用约定)ENVI^ memvar=[?返回名,][:字节数:]偏移,值修改/查询 PECMD 内存变量ENVI^ LoadPlugin=basename加载插件ENVI^ DisX64=1[,Old]禁用/恢复 WOW64 文件系统重定向ENVI^ zero=0|1私密模式:内存用完清零ENVI^ Arg=*[字符集]字符串词语分断ENVI^ Clipboard=text|?=var读写剪贴板ENVI^ EnviBroad=0|1|-环境变量广播开关ENVI^ __arg=0|1兼容模式:启用 &&__arg 参数表ENVI^ LoadEnvi [路径|-] [变量名]从注册表刷新环境变量ENVI^ HelpColor=[*高度] [前景色][#背景色]HELP 显示颜色ENVI @@DeskTopFresh=[clearicon][;][1|2|4|8|16][;[-+]path]桌面刷新ENVI @@TaskIcoMenu=0|1|2托盘菜单切换(0=关闭, 1=开启, 2=切换)
完整 ENVI^ 控制命令、二进制缓冲区操作(SET-cmp、SET-tom、SET-copy、SET?int 等)和函数参数引用,参见 commands-full.md。
ENVI-env 和 SET-env 后缀临时绕过 ForceLocal=1 限制,直接操作环境变量。常用于线程内读取父窗口上下文的环境变量(如 __WinID):
wcs
ENVI-env hwnd0=%&__WinID% // 从环境变量读取 __WinID(绕过 ForceLocal)
SET-env MyName=%MyName% // 临时设为环境变量
wcs
#code=65001
ENVI^ EnviMode=1
ENVI^ ForceLocal=1
SET$ &NL=0d 0a
SET$ &TAB=09
wcs
SET$ &NL=0d 0a // 十六进制转宽字符串(Unicode)
SET$# &buf=*4096 0 // 十六进制转原始字节(二进制缓冲区)
ENVI$ &data=*1M 30 0d 0a // 可变长度十六进制分配
wcs
_SUB 函数名 [*] // * = this-call(在调用者栈中运行)
SET ¶m1=%~1 // %~1 剥离外层引号
ENVI-ret %~3=%&result% // 通过引用参数返回值
// 函数参数:%0=函数名, %1~%n=各参数, %#=参数个数, %*=全部参数, %~=去掉引号
_END
text
CALL 函数名 [参数] // 调用函数
CALL *函数名 [参数] // this-call(调用者栈)
CALL @窗口名 [参数] // 创建/显示窗口(模态,阻塞)
CALL @*窗口名 [参数] // 并行窗口(可同时操作,但关闭前阻塞后续命令)
CALL @-窗口名 [参数] // 后台窗口
CALL @~窗口名 [参数] // 后台,完全非阻塞
CALL @~~窗口名 [参数] // 后台快速调用(非阻塞)
CALL @^窗口名 [参数] // 并行,父窗口不阻塞子窗口
CALL @+窗口名 [参数] // 弃养子窗口
CALL @-- // 销毁 Win 环境(无参数)
CALL @--popmenu 窗口名 [x.y[:对齐]] // 在指定位置弹出菜单(对齐:对齐方式)
CALL --mem &变量名 [*] [参数] // 执行内存中的动态函数代码
wcs
_SUB 窗口名,L200T100W400H300,窗口标题,[关闭命令],[图标],[样式],[遮罩] [-标志1 -标志2 ...]
_END
窗口形状: L<左>T<上>W<宽>H<高>。省略 L/T 则居中。
窗口样式: - = 无标题栏, # = 无边框,数字 1-99 = 透明度, : 透明色。
常用标志: -trap(关闭按钮不退出)、 -nocap(无标题栏)、 -nosysmenu、 -top、 -size、 -maxb、 -disminb、 -discloseb、 -nfocus、 -ntab、 -forcenomin、 -scalef、 -scale[:DPI]、 -nxp、 -csize、 -na、 -layer(支持渐透明)、 -disaltmv(禁用 ALT 拖动)
wcs
IMPORT 路径\库文件.wcs // 将可复用函数加载到当前脚本
_ENDFILE-IMPORT // 此行以下内容在 IMPORT 时被丢弃
{ // 带独立 PE 变量栈的代码块
SET temp=仅此处有效
}
{* // this-call 代码块(在调用者栈中运行)
LOCK .ole // COM 初始化等需要在当前作用域执行的操作
}
文件级和函数级的 { 必须从第 1 列开始。嵌套 _SUB 用双冒号访问: 类名::子函数名。点号用于数据成员访问(如 类名.属性)。
*c 后缀区分)#wcs
FIND $%var%=hello, 命令 // 相等(不区分大小写)
FIND $%var%*c=hello, 命令 // 相等(区分大小写,*c 后缀)
FIND $%var%<>hello, 命令 // 不等
FIND $%var%=, 命令 // "为空" 测试(惯用法)
FIND *=var, 命令 // 惯用法:"为空"
FIND *<>var, 命令 // 惯用法:"非空"
FIND |%a%>%b%, 命令 // | 前缀 = 浮点数比较
FIND #%a%>%b%, 命令 // # 前缀 = 整数比较(INT64)
FIND [ $A & $B ], 命令 // 复合 AND
FIND [ $A | $B ], 命令 // 复合 OR(| 分隔)
FIND --pid &var, // 返回 5 值:空闲时间 总时间 CPU个数 1秒时钟数 一时钟100ns数
FIND --pid*@ &var, // 进程列表(用于 TABL)
FIND --class:Shell_TrayWnd --wid*@ &var // 按类名过滤窗口列表
FIND --forpid:PID --wid*@ &var // 按进程 ID 过滤窗口
FIND --sub --wid*@ &var // 递归子窗口
FIND --menu &var,窗口ID // 查询窗口的 MENU 句柄
FIND --menu#Index &var,MenuID // 按索引查询子 MENU
wcs
IFEX C:\boot.ini, 命令 // 文件/目录存在
IFEX C:\boot.ini,! 命令 // 不存在
IFEX x:\, 命令 // 盘符存在且有文件系统
IFEX $%val%>=5, 命令 // $=浮点数比较(#=整数比较,|=字符串比较)
IFEX [ 条件1 & 条件2 ], 命令 // AND 复合条件
IFEX [ 条件1 | 条件2 ], 命令 // OR 复合条件(| 分隔)
IFEX MEMU=?,&var // 查询可用内存
IFEX C:\=?,&可用空间 // 查询磁盘可用空间
wcs
SET &&I=0
LOOP #%&I%<=10, { MESS %&I% | CALC &I=%&I% + 1 }
FORX * %&list%,&&item, { MESS %&item% } // * = 空格分隔
FORX *NL &多行变量,&&line, { ... } // *NL = 换行分隔
FORX *NL:| &数据,&&item, { ... } // *NL:分隔符 = 自定义分隔符
FORX *L 0 2 10,&&val, { ... } // *L = 数值循环(起始 步长 结束)
FORX /S C:\Windows\*.exe,&&file,0 { ... } // 文件系统枚举
FORX @\Windows,&&winDir,1 { ... } // @=仅搜索目录,\=所有盘符
// ⚠ FORX 循环变量引用规则:
// 定义用 &&item,引用值用 %&item%(单&),**不是** %&&item%
// 示例:FORX *NL &list,&&line, { SET &val=%&line% } // ✅ 正确
// FORX *NL &list,&&line, { SET &val=%&&line% } // ❌ 错误
TEAM SET &a=1| SET &b=2| CALC &c=%&a% + %&b% // 多命令链
// 线程
THREAD* CALL WorkerFunc // * = 持久栈(共享变量,保持父子关系)
THREAD CALL WorkerFunc // 独立模式(复制变量到子线程)
THREAD& CALL WorkerFunc // & = 强制 PE 变量模式(最简多线程)
THREAD+ CALL WorkerFunc // + = 抛弃式线程(不等待退出)
THREAD# CALL WorkerFunc // # = 代理模式(线程结束则退出)
THREAD $ CALL WorkerFunc // $ = 预先解释命令组
THREAD -link CALL WorkerFunc // 保持父子关系(类似 *)
THREAD -wait CALL WorkerFunc // 等待完成(阻塞消息循环)
THREAD -waitx CALL WorkerFunc // 等待完成(不阻塞消息循环)
THREAD -here CALL WorkerFunc // 当前栈的孩子(可修改父栈临时PE变量)
THREAD -waitp CALL WorkerFunc // 进程结束前等待该线程
THREAD -tid:&tid CALL WorkerFunc // 获取线程 ID
THREAD --st:128K CALL WorkerFunc // 设置栈大小
{ LOCK #pecmd // 原子作用域
LOCK --exist #MyLock,&&ret // 检查锁是否存在
}
LOCK #MyLock,&&ret2 // 创建/获取命名锁
完整 EXIT 变体、LAMBDA 语法和 FIND --class: 参见 commands-full.md。
控件命令控件命令按钮ITEM标签LABE编辑框EDIT复选框CHEK单选框RADI下拉列表LIST表格TABL进度条PBAR分组框GROU图片IMAG多行文本MEMO子窗口SWIN定时器TIME托盘/气泡TIPS / TIPS*选项卡TABS滑块SLID微调器SPIN树形视图TREE滚动条SBAR热键HKEY日期时间DTIMIP 地址IPAD菜单项MENU屏幕捕捉SCRN浏览窗口BROW
wcs
ENVI @控件.MSG=_%&WM_LBUTTONDOWN%: 命令 // _ = 后置系统处理器(控件通知)
ENVI @窗口.MSG=0x0010: CALL OnClose // 无 _ = 窗口级消息(WM_CLOSE)
ENVI @控件.MSG=_%&msg%::&&wp,&&lp, CALL Handler // 捕获 wParam/lParam
ENVI @控件.POSTMSG=#1 // 投递自定义消息 #1
ENVI @控件.SENDMSG=消息号;wParam;lParam // 同步发送
ENVI @控件.MSG=$0x0201: 命令 // $ = 屏蔽系统响应(可返回结果码)
ENVI @控件.MSG=*msg: 命令 // * = 捕鼠器 B(鼠标捕获模式)
ENVI @控件.MSG=+msg: 命令 // + = 超级捕获
MSG 前缀说明:无前缀=直接处理后丢弃(不传递给系统), _ =系统响应后执行(控件通知), $ =屏蔽系统响应(可返回结果码), * =捕鼠器 B(鼠标捕获), + =超级捕获。消息号 *del 删除映射。 #1 ~ #N 为 PECMD 自定义消息。
wcs
ENVI @控件名=新文本 // 设置文本
ENVI @控件名.Enable=0 // 禁用(1=启用)
ENVI @控件名.Visible=0 // 隐藏(1=显示)
ENVI @控件名.POS=左:上:宽:高 // 移动/调整大小
ENVI @控件名.POS=?;&L:&T:&W:&H // 查询位置
ENVI @控件名.Val=?行.列;&var // 获取 TABL 单元格
ENVI @控件名.Val=?*;&count // 获取 TABL 行数
ENVI @控件名.*del= // 销毁控件
完整控件语法、全部 25 种控件类型、ENVI @ 属性参考、窗口生命周期和消息映射——参见 pecmd-gui.md。
GUI 写法示例(动态控件、选项卡页、自定义标题栏、GDI 绘图、拖放等)——参见 how-tos/gui.md。
实测验证(32-bit / 64-bit 行为一致): 以下内容在 PECMD 32-bit (PECMDx86) 和 64-bit (PECMD) 上均经实测确认,行为完全相同。DLL 调用可正常工作,但有重要限制。
DLL 函数 默认按 PASCAL/stdcall 调用约定。Windows API(WINAPI 标记)不需要 --c。使用 --c 切换到 CDECL。最多 20 个参数(C 调用 100 个)。
wcs
// ✅ 正确格式:逗号分隔,整数 #,字符串 $,建议加 --qd
CALL $ --qd --ret:&&r user32.dll,GetSystemMetrics,#0 // 整数参数
CALL $ --qd --ret:&&r user32.dll,FindWindowW,$Progman,#0 // 字符串+整数
CALL $ --qd --ret:&&r kernel32.dll,lstrlenW,$Hello World // 字符串参数
CALL $ --ret:&&hDll ,-LoadLibrary,^user32.dll // 加载 DLL(^ = 自动释放)
CALL $ --ret:&&r *%&hDll%,GetSystemMetrics,#0 // 通过句柄调用
CALL $ ,-FreeLibrary,*%&hDll% // 释放 DLL
CALL $ --bool --ret:&&ret DLL,函数,... // --bool:函数返回 BOOL
CALL $ --cd --ret:&&ret DLL,函数,... // --cd:执行前切换到目标目录
// Win32 回调函数绑定(用于 EnumResourceNames 等 API)
ENVI^ WndProc1C,WndProc1Addr // C 调用约定回调
SET^ WndProc1,WndProc1Addr // PASCAL/stdcall 回调
// PECMD 自动查找 _SUB OnWndProc1 并包装为 Win32 回调
Func.#Param)完全不工作# 前缀——无前缀返回 0$ 前缀--qd——否则字符串多传一个 null 终止符,导致 API 结果错误:字符串无 --qd有 --qd说明$AlstrlenW → 2lstrlenW → 1无 --qd 含 null$TestlstrlenW → 5lstrlenW → 4无 --qd 含 null$ProgmanFindWindowW → 0FindWindowW → 66062无 --qd 找不到窗口
wcs
// ❌ 点语法完全不工作
CALL $--qd --ret:&&r user32.dll,GetSystemMetrics.#0 // 返回空
// ❌ 整数无前缀返回 0
CALL $ --ret:&&r user32.dll,GetSystemMetrics,0 // 返回 0
// ⚠ GetProcAddress:无 --qd 时常返回 0x0,加 --qd 后可工作
CALL $ --ret:&&p ,-GetProcAddress,*%&hDll%,$GetSystemMetrics // 可能返回 0x0
CALL $--qd --ret:&&p ,-GetProcAddress,*%&hDll%,GetSystemMetrics // 加 --qd 更可靠
// ⚠ 缓冲区输出:无 --qd 时数据可能不回传,加 --qd 后可工作
SET$ &buf=*256 0
CALL $--qd --ret:&&r kernel32.dll,GetWindowsDirectoryW,*&buf,#256 // --qd 模式
// ❌ 无 --qd 时字符串含 null 终止符,FindWindowW 等失败
CALL $ --ret:&&r user32.dll,FindWindowW,$Progman,#0 // 返回 0
场景语法状态无参整数返回CALL $ --ret:&&r kernel32.dll,GetTickCount✅整数参数CALL $ --qd --ret:&&r user32.dll,GetSystemMetrics,#0✅字符串输入CALL $ --qd --ret:&&r user32.dll,FindWindowW,$Progman,#0✅变量作参数CALL $ --qd --ret:&&r kernel32.dll,lstrlenW,$%&str%✅LoadLibraryCALL $ --ret:&&h ,-LoadLibrary,^user32.dll✅通过句柄调用CALL $ --ret:&&r *%&h%,GetSystemMetrics,#0✅缓冲区输出加 --qd 后可回传到 PE 变量⚠GetProcAddress加 --qd 后可工作⚠点语法Func.#Param 不解析❌
项目PECMDx86 (32-bit)PECMD (64-bit)文件标识PE32, i386PE32+, x86-64bX64 值0(32 位系统)或 1(64 位系统)3指针大小4 字节(< 4GB)8 字节(> 4GB)DLL 调用行为与 64-bit 完全相同与 32-bit 完全相同LoadLibrary 句柄0x761A0000``0x7FFC34B00000代码差异无无
bX64 值含义: 0 =WIN32(32 位系统原生), 1 =WIN64(64 位系统运行 32 位 PECMD), 3 =PECMD64(64 位 PECMD)。
前缀类型描述示例#integer按整数传递#0、 #256、 #%&hwnd%``<INT6464 位整数<0x100000000``$string按 Unicode 字符串传递(配合 --qd 不含 null)$CabinetWClass``@ANSI按 ANSI 字符串传递@text``*buffer传递 PE 变量地址( *&buf 形式可回传输出)*&buf``=raw按原始数据传递~deref间接解引用(展开变量名再读值)
wcs
SET$# &buf=*256 0 // 分配 256 字节原始缓冲区
SET$ &strBuf=*256 0 // 分配 256 个宽字符
SET-long &buf=value:offset // 写入 32 位整数到缓冲区偏移
SET?int &buf=&&var:offset // 从缓冲区偏移读取 32 位整数
DLL 缓冲区输出可使用 *&buf 形式(PE 变量地址传递,输出回传)。获取系统信息也可用内置命令:
FIND --wid*@ &var — 枚举窗口(返回 HWND、类名、标题)FIND --pid*@ &var — 枚举进程REGI — 注册表读取系统信息EXEC* &out=!cmd.exe /c ... — 捕获外部命令输出IFEX MEMU=?,&var — 查询可用内存IFEX C:\=?,&var — 查询磁盘空间wcs
IFEX #%&::bX64%=3, SET &PtrSz=8! SET &PtrSz=4 // 3=PECMD64, 1=WIN64(32位PECMD在64位系统), 0=WIN32
wcs
FDRV &盘符列表=*: // 枚举所有盘符
FDRV *vol &卷标,&文件系统=C: // 获取卷标和文件系统
PART list disk,&&全部磁盘 // 枚举所有物理磁盘
PART list disk %&dsk%,&&磁盘信息 // 获取磁盘信息
PART list part %&dsk%,&&分区列表 // 列出分区号
PART -hextp -phy# list part %&dsk%#%&pt%,&&分区信息 // 详细分区信息
SHOW * %&dsk%#%&pt%,%&盘符% // 分配盘符
SHOW *- %&dsk%#%&pt%, // 移除盘符
wcs
REGI $HKLM\SOFTWARE\App\Key,&var // 读 REG_SZ
REGI #HKLM\SOFTWARE\App\Count,&var // 读 REG_DWORD
REGI $HKLM\SOFTWARE\App\Key=值 // 写 REG_SZ
READ %路径%,**,&内容 // 读取整个文件
WRIT %路径%,+0,新行 // 追加一行
GETF# %路径%,0#*,&原始数据 // 按原始字节读取文件
wcs
EXEC* &输出=!cmd.exe /c dir /b // 捕获全部输出
EXEC =!"%MyNAME%" TEAM WAIT 1000|LOAD other.ini // 运行子 PECMD
EXEC* &out=!cmd.exe /c ipconfig // 捕获外部命令输出
EXEC* --exe:#101 &out=*embedded.exe // 从 PECMD 资源运行 EXE
wcs
{ LOCK #pecmd
LOCK --exist #MyAppLock,&&exists
}
IFEX $1=%&exists%,
{
REGI $HKCU\Software\MyApp\WID,&&wid
IFEX $%&wid%>0, TEAM ENVI @@Visible=%&wid%:2| ENVI @@POS=%&wid%:::::::1
EXIT FILE
}
LOCK #MyAppLock,&ret2
wcs
MSTR &&a,&&b=<1><3->%&data% // 字段1、字段3到末尾(- = 到末尾)
MSTR &&last=<-1>%&data% // 最后字段(负索引)
MSTR &&q=<~5>%&data% // 字段5并剥离外层引号(~前缀)
MSTR -delims:. &&a,&&b,&&c,&&d=<1><2><3><4>%&ip% // 按自定义分隔符拆分
MSTR* &&a,&&b=<1><2>%&data% // *前缀 = TAB 分隔
MSTR$ &&a,&&b=<1><2>%&data% // $前缀 = 空格分隔(连续空格视为单个)
SED &&r=0,模式,替换,%&source% // 替换所有匹配(正则模式)
SED &&r=1,模式,替换,%&source% // 替换第一个匹配
SED &&r=-1,模式,替换,%&source% // 替换最后一个匹配(负数=从末尾)
SED -t &&r=0,ABCD,abcd,%&source% // -t = 字符集翻译(A→a, B→b, 逐字符映射)
SED -ni &&r=0,模式,替换,%&source% // -ni = 不区分大小写
SED &&ext=-1,.*\.,, ,%&filename% // 获取扩展名(.*\. 匹配到最后一个句号)
LPOS &&pos=needle,,%&haystack% // 查找首次出现(不区分大小写)
MSTR 注意事项: 默认分隔符为空格。
-delims:09指定制表符(十六进制 09)。字段索引从 1 开始。<N>= 单个字段,<N->= 字段N到末尾,<N*>= 仅字段N(单字段),<~N>= 剥离引号,<-N>= 从末尾反向索引。MSTR*= TAB分隔,MSTR$= 空格分隔(连续空格视为单个)。如果 MSTR 返回空,先用WRIT打印源变量确认格式,或改用FORX *按空格逐字段拆分作为替代。SED 注意事项: SED 默认使用正则模式。
.匹配任意字符,非字面量句号。匹配字面量句号用\.。使用标志字符*可切换为字面量模式(不解释正则)。count=0 替换全部匹配,count=N 替换前 N 个,count=-N 替换后 N 个。0:0与0等价。-t标志启用字符集翻译,-ni标志不区分大小写。模式中的空格作为参数分隔符处理时可能引起问题——用变量间接传递含空格的模式。LPOS 注意事项: 参数顺序为
LPOS &&pos=needle,,haystack(查找目标在前,源字符串在后)。返回首次出现的位置索引。加,1,可区分大小写。RPOS从末尾查找。
含错误处理和边界情况处理的完整展开版本,参见 how-tos/storage.md(磁盘/注册表/文件)、 how-tos/system.md(进程/线程/定时器)、 how-tos/net.md(网络/COM)。
wcs
CALC &结果=%&a% + %&b% // 基本算术:+ - * / % ^
CALC #&结果=%&a% & %&b% // # = 整数模式;位运算:& | @
CALC -base=16 #&hex=shl(0x07,16)|0x20 // 十六进制输出 + 位移
CALC &sz=%&bytes%/1G#3 // 字节转 GB,3 位小数
CALC -err=0 &r=%&a% / %&b% // 出错时返回默认值 0
数学函数(34个):
类别函数三角sin cos tan ctg arcsin arccos arctan arcctg deg rad代数abs sqrt ln lg log exp pow pow10 hypot(⚠ log() 需两参数 log(底数,值),单参数返回 0;用 lg() 代替 log₁₀)取整floor ceil round int frac位运算shl shr xor not lnot其他div mod rand max min
常量: e、 pi。尺寸后缀: K =1024、 M =1024²、 G =1024³、 T =1024⁴、 S =512。
多表达式用 ; 分隔,子变量 $subName=expr。
CALC &J=1+2 可以正常执行。右侧以 %&I% 等变量开头时,用空格分隔( CALC &J= %&I%+1)。PECMD 要求减号后必须有空格( 3 - 2)。//、 ;、 `(反引号)均为有效注释符。行尾注释前必须有空格(如 SET &a=1 // 注释)。help.txt: "注释符前有一个空字符,该空字符算注释"——所有位置的注释符前都建议加空格。SET var=val 语义上等价于 ENVI &var=val。启用 ForceLocal=1 后,两者都创建局部 PE 变量。$ = 字符串比较、 | = 浮点比较;IFEX 中 $ = 浮点比较、 | = 字符串比较。 # 在两者中均为整数比较。IFEX 中 | 在 [] 复合条件内为 OR 逻辑运算符。FDRV、 FORM、 FIND C:\=? 都需要 : 后缀。LOAD "C:\Program Files\a.ini" 需要引号。#code=65001 = UTF-8, #code=936 = GBK)且文件编码须与声明一致。SDK 示例普遍使用 GBK(936)。{ 位置:文件级和函数级 { 必须从第 1 列开始。在 TEAM/LOOP/IFEX 内部, { 启动命令组。\ 时,将该行合并到上一行。_SUB、 WRIT、 LOOP 不能内联:不能在 FIND/IFEX/TEAM 命令内部定义 _SUB。 WRIT 和 LOOP 也必须位于单独一行,不能嵌套在 FIND/IFEX/TEAM 内。FIND $%var%=, 测试"为空"。 FIND *=var, 也测试"为空"(惯用法)。 FIND *<>var, 测试"非空"。% 用 %%。* 函数)中的变量是 共享 的(非复制);非持久上下文(普通函数、 {} 块)中的变量自动复制一份。真正跨线程通信用 &:: 变量。%SystemRoot%\System32\OnShutdown.wcs,格式为 OnShutdown.wcs <操作码> [脚本参数表]。操作码: shutdown =关机、 reboot =重启、 logout =注销、 suspend =挂起、 hiber =休眠、 poweroff =关电、 lock =锁定计算机、 unknown =未知。关机菜单支持的子集: shutdown、 reboot、 logout、 poweroff、 unknown。^ 预解释: ^COMMAND 将变量展开推迟到执行时(循环中必备)。 ^^COMMAND 预解释两次。_ 前缀:控件通知用 _msg#;窗口级消息省略 _。搞错这一点是非常常见的错误。{} 块中,两者都复制。FIND $%&var%=值 引用 PE 变量。@@Visible 与 @Visible:跨进程用 ENVI @@Visible=窗口ID:值。进程内用 ENVI @控件.Visible=0|1。FORX ... &&item, { } 定义用 &&item,循环体内引用值用 %&item%(单&)。用 %&&item% 会找不到变量。序号 窗口ID 控件ID 父窗口ID 线程ID 进程ID 类型 标题,字段用 TAB 分隔。配合 FORX *NL 按行遍历、 MSTR* 按 TAB 分字段提取(field 2=窗口ID, field 7=类型, field 8=标题)。FIND --wid*@(窗口枚举)、 FIND --pid*@(进程枚举)、 REGI(注册表)、 EXEC*(外部命令)替代。Visible 与 Visable:两者均可工作(PECMD 接受两种拼写)。文档统一用 Visible,但 SDK 示例普遍用 Visable。FORX *NL:| &data,&&item, 用 | 作为行分隔符(默认为换行)。ENVI^ ALIAS GetTick=CALL $ --ret:&&r kernel32.dll,GetTickCount 使 GetTick 可作为伪命令直接调用。当需要超越上述快速参考的详细信息时,查阅以下权威文件:
文件何时查阅references/commands-full.md命令语法、全部标志、参数细节references/pecmd-gui.mdGUI 控件、消息、窗口生命周期、ENVI @ 属性references/pe-startup.mdWinPE 启动脚本、PECMD.INI 结构、启动阶段references/how-tos/storage.md磁盘、分区、文件、注册表、设备 写法示例references/how-tos/system.md进程、线程、定时器、系统信息、加密、工具references/how-tos/gui.mdGUI 模式:动态控件、选项卡、GDI、拖放等references/how-tos/net.md网络、SOCK、COM/WMI 写法示例references/troubleshooting.md常见错误排查、DLL 调用失败、变量引用问题
编写 PECMD 脚本和工具时遵循以下规范:
#code=65001,随后 ENVI^ EnviMode=1,再 ENVI^ ForceLocal=1&变量名)TEAM 链式组合紧凑的初始化序列_SUB 函数ENVI @控件.POS=? 保存初始控件位置,在 resize 事件中恢复EXEC =!"%MyNAME%" ... 或 EXEC* &out=!cmd.exe /c ...&:: 类 PE 变量{ LOCK #pecmd ... } 块包装互斥操作_ 前缀( _0x0201);窗口级消息省略 _( 0x0010)SED 做正则替换(始终正则模式, . 匹配任意字符, \. 匹配字面量句号),用 MSTR 从结构化输出中提取字段——如果 MSTR 不工作,改用 FORX * 逐字段拆分GitHub: https://github.com/VirtualHotBar/PECMD-Pro-Max ClawHub: https://clawhub.ai/virtualhotbar/pecmd-pro-max