
go 标准库不支持直接枚举 unc 路径(如 `\computera`)下的共享文件夹,需借助系统命令(如 windows 的 `net view`)并解析其输出来实现跨平台或目标平台的共享发现。
在 go 中,os.ReadDir(或旧版 ioutil.ReadDir)仅能读取已知共享路径(如 \ComputerASource)下的文件和子目录,但无法列出远程主机根级的共享名列表(即 \ComputerA 本身)。这是因为 UNC 根路径 \ComputerA 并非一个常规文件系统目录,而是一个 SMB 共享枚举入口——该操作需依赖底层操作系统提供的网络发现协议(如 SMB/CIFS 的 NetShareEnum),而 Go 标准库(os、net、io/fs 等)并未封装此类功能。
✅ 正确做法是调用宿主系统的原生命令:
- windows:使用 net view \ComputerA(注意双反斜杠需在 Go 字符串中转义为 \\ComputerA)
- linux/macOS(需安装 smbclient):使用 smbclient -L ComputerA -N(-N 表示无密码登录,适用于允许匿名浏览的环境)
以下是一个 Windows 平台的 Go 示例实现:
package main import ( "fmt" "os/exec" "strings" "syscall" ) func listSharesOnWindows(host string) ([]string, error) { cmd := exec.Command("net", "view", "\\"+host) cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} out, err := cmd.Output() if err != nil { if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 2 { return nil, fmt.Errorf("computer '%s' not found or unreachable", host) } return nil, fmt.Errorf("failed to run 'net view': %w", err) } var shares []string for _, line := range strings.Split(string(out), "n") { // 共享名通常位于行首,以字母/数字开头,后跟若干空格和描述 fields := strings.Fields(line) if len(fields) >= 2 && strings.HasSuffix(fields[1], "Disk") { shareName := fields[0] // 过滤掉标题行(如 "Share name")和分隔线 if shareName != "Share" && shareName != "-------" { shares = append(shares, shareName) } } } return shares, nil } func main() { shares, err := listSharesOnWindows("ComputerA") if err != nil { fmt.Printf("Error: %vn", err) return } fmt.Printf("Found shares on ComputerA: %vn", shares) // 示例输出: Found shares on ComputerA: [Source Data Backup] }
⚠️ 注意事项:
- net view 需要目标计算机启用「网络发现」和「文件和打印机共享」,且防火墙允许 SMB 流量(TCP 445);
- 若目标主机启用了严格签名或拒绝匿名访问,可能需要凭据——此时可结合 cmd.Stdin 输入用户名/密码,或改用支持认证的第三方库(如 github.com/StackExchange/wmi 在 Windows 上通过 WMI 查询);
- Linux/macOS 用户应优先考虑 smbclient -L,但需确保 samba-client 已安装,并注意权限与 SMB 版本兼容性(例如添加 -m SMB2 强制协议);
- 切勿将 net view 输出直接用于生产级路径拼接——应校验共享名格式(避免空格、特殊字符注入),并始终对后续 os.ReadDir(“\\ComputerA\ShareName”) 调用做错误处理。
总结:Go 本身不提供跨平台 SMB 共享发现能力,必须桥接系统工具。这是权衡简洁性与功能性的典型场景——用标准库处理「已知路径」,用 exec.Command 处理「服务发现」。