
本文介绍在 go 语言中获取 windows 当前所有已安装服务(含运行/停止状态)的实用方法,由于标准库不支持,需通过调用系统命令 `sc query state=all` 并解析其输出实现。
在 Go 的标准库中,没有跨平台或 Windows 专用的 API 用于枚举系统服务——这与获取进程列表(如 pslist 或 tasklist)不同,os/exec 包虽可执行外部命令,但服务管理属于 Windows 特有的 SCM(Service Control Manager)范畴,Go 官方明确不会将其纳入标准库(参见 Go issue #12987)。
因此,推荐方案是:调用 Windows 内置命令行工具 sc.exe,并结构化解析其输出。最常用命令为:
sc query state= all
该命令会列出所有服务(无论状态),每项包含 SERVICE_NAME、DISPLAY_NAME 和 STATE(如 RUNNING、STOPPED、PAUSED)等关键字段。
以下是一个完整、健壮的 Go 示例代码,使用 os/exec 执行命令,并按服务块(以 [SC] 开头的分隔行为界)解析结果:
package main
import (
"bufio"
"fmt"
"os/exec"
"regexp"
"strings"
)
type Service struct {
Name string
DisplayName string
State string // e.g., "RUNNING", "STOPPED"
}
func listWindowsServices() ([]Service, error) {
cmd := exec.Command("sc", "query", "state=all")
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("failed to run sc command: %w, output: %s", err, string(output))
}
var services []Service
scanner := bufio.NewScanner(strings.NewReader(string(output)))
reName := regexp.MustCompile(`SERVICE_NAME:\s+(.+)`)
reDisp := regexp.MustCompile(`DISPLAY_NAME:\s+(.+)`)
reState := regexp.MustCompile(`STATE\s+:\s+(\d+)`)
var current Service
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
if matches := reName.FindStringSubmatchIndex([]byte(line)); matches != nil {
current.Name = strings.TrimSpace(string(line[matches[0][1]:]))
} else if matches := reDisp.FindStringSubmatchIndex([]byte(line)); matches != nil {
current.DisplayName = strings.TrimSpace(string(line[matches[0][1]:]))
} else if matches := reState.FindStringSubmatchIndex([]byte(line)); matches != nil {
// Convert numeric state (e.g., 4 → RUNNING) via known mapping
stateNum := strings.TrimSpace(string(line[matches[0][1]:]))
current.State = map[string]string{
"1": "STOPPED",
"2": "START_PENDING",
"3": "STOP_PENDING",
"4": "RUNNING",
"5": "CONTINUE_PENDING",
"6": "PAUSE_PENDING",
"7": "PAUSED",
}[stateNum]
if current.State == "" {
current.State = "UNKNOWN"
}
services = append(services, current)
current = Service{} // reset for next service
}
}
return services, scanner.Err()
}
func main() {
svcs, err := listWindowsServices()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Found %d services:\n", len(svcs))
for i, s := range svcs[:min(10, len(svcs))] { // show first 10
fmt.Printf("%d. %s (%s) → %s\n", i+1, s.Name, s.DisplayName, s.State)
}
if len(svcs) > 10 {
fmt.Printf("... and %d more\n", len(svcs)-10)
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}✅ 注意事项与最佳实践:
- ✅ 权限要求:sc query 通常无需管理员权限即可读取服务列表,但若需访问某些受保护服务(如 TrustedInstaller 托管服务),可能返回空或报错;
- ✅ 输出稳定性:sc 命令输出格式相对稳定(微软文档保证),但建议避免强依赖空格对齐,优先使用正则匹配关键词后内容;
- ✅ 替代方案:如需更高可靠性或更丰富信息(如启动类型、账户、描述),可考虑调用 Windows API(通过 syscall 或第三方库如 [golang.org/x/sys/windows](https://www.php.cn/link/de4ed7bfb03191c49f42e9b66fcfade1;
- ⚠️ 跨平台限制:此方法仅适用于 Windows;Linux/macOS 需分别使用 systemctl list-units --type=service 或 launchctl list,无法统一。
总结:在 Go 中获取 Windows 服务列表,os/exec + sc query state=all 是最轻量、可靠且无外部依赖的方案。合理封装解析逻辑后,即可无缝集成至监控工具、部署脚本或系统诊断程序中。










