
go 标准库不支持直接枚举 unc 路径(如 `\\computera`)下的共享文件夹,需借助系统命令(如 windows 的 `net view`)并解析其输出来实现跨平台或目标平台的共享发现。
在 Go 中,os.ReadDir(或旧版 ioutil.ReadDir)仅能读取已知共享路径(如 \\ComputerA\Source)下的文件和子目录,但无法列出远程主机根级的共享名列表(即 \\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: %v\n", err)
return
}
fmt.Printf("Found shares on ComputerA: %v\n", 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 处理「服务发现」。










