
在 go 中通过 exec.command 调用 iis appcmd 时,因双引号被 go 运行时自动转义,导致 appcmd 解析参数失败并报“invalid xml input”错误(hresult=c00cee3a);需绕过默认参数转义机制,使用 syscall.sysprocattr.cmdline 直接构造完整命令行。
在 go 中通过 exec.command 调用 iis appcmd 时,因双引号被 go 运行时自动转义,导致 appcmd 解析参数失败并报“invalid xml input”错误(hresult=c00cee3a);需绕过默认参数转义机制,使用 syscall.sysprocattr.cmdline 直接构造完整命令行。
在 Windows 环境下,Go 的 os/exec 包对带空格和引号的命令参数会进行自动转义(尤其在 exec.Command("cmd", "/C", "...") 形式中),这会导致 IIS appcmd.exe 接收到的 /site.name:"My website" 参数被破坏——例如引号被重复转义或丢失,进而使内部 XML 解析器误判输入格式,最终触发 exit status 3222072890(对应 HRESULT 0xc00cee3a,即 ERROR_SXS_XML_E_BADXML)。
标准写法(❌ 错误):
out, err := exec.Command("cmd", "/C", `%windir%\system32\inetsrv\appcmd list APP /site.name:"My website" /text:[path='/'].physicalPath`).Output()
该方式将 "My website" 作为独立参数传入 cmd,Go 会尝试用 \ 或 " 转义,破坏了 appcmd 所需的原始字符串结构。
✅ 正确解决方案:禁用 Go 的参数解析,直接指定完整命令行字符串
利用 syscall.SysProcAttr.CmdLine 字段,让操作系统直接执行指定的命令行(不经过 Go 的 argv 拆分与转义):
package main
import (
"fmt"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("cmd")
cmd.SysProcAttr = &syscall.SysProcAttr{
CmdLine: `/C %windir%\system32\inetsrv\appcmd list APP /site.name:"My website" /text:[path='/'].physicalPath`,
}
out, err := cmd.Output()
if err != nil {
fmt.Printf("Error: %v\nOutput: %s\n", err, out)
return
}
fmt.Printf("Result: %s", out)
}
⚠️ 关键注意事项:
-
CmdLine必须是完整、可执行的命令字符串(包括/C),且需使用反斜杠路径(Windows 原生要求); - 不要混用
exec.Command(name, args...)和SysProcAttr.CmdLine—— 后者会完全忽略args; - 确保运行 Go 程序的账户具有 IIS 配置读取权限(如属于
IIS_IUSRS或管理员组); - 若网站名含特殊字符(如
&,, <code>>),建议先 URL 编码或预校验,但 appcmd 本身不支持 URL 解码,应优先避免在站点名中使用 XML 敏感字符; - 在 IIS 8.5+ 中,
/text:输出为纯文本,无需额外 XML 解析,但路径表达式[path='/']必须严格匹配语法(方括号、单引号不可省略)。
? 小技巧:调试时可在 CmdLine 中追加 > C:\temp\appcmd.log 2>&1 重定向输出,或改用 /C ... && pause 临时保留控制台窗口,直观查看原始错误信息。
此方案本质是回归 Windows 命令行原语执行模型,规避了 Go 1.6+ 中因安全加固引入的过度转义逻辑(详见 Go Issue #15566)。对于所有依赖精确引号/空格语义的 Windows 原生命令(如 netsh, dism, wmic),该模式均适用。











