如何在 Go 中发现局域网中指定计算机的共享文件夹

8次阅读

如何在 Go 中发现局域网中指定计算机的共享文件夹

本文介绍在 go 程序中探测远程 windows 计算机(如 computera)所公开的共享文件夹(smb 共享)的方法,由于标准库不支持 unc 根路径枚举,需借助系统命令(如 `net view`)并安全解析其输出。

go 的标准库(如 os, io/ioutil(已弃用,推荐 os.ReadDir)或 net 包)无法直接枚举远程主机的共享列表。调用 os.ReadDir(“\\ComputerA”) 会失败,因为 UNC 路径 \\ComputerA 并非一个可遍历的“目录”,而是一个 SMB 共享命名空间入口——该操作需由 windows 网络重定向器(RDR)配合 Server Message Block (SMB) 协议的 NetShareEnum 类底层 API 完成,而 Go 标准库未封装此功能。

因此,跨平台兼容性虽受限,但在 Windows 环境下最实用、可靠的方式是调用系统内置命令:

✅ 推荐方案:使用 net view 命令(Windows)

net view ComputerA 可列出目标计算机发布的所有共享资源(含共享名、类型和备注)。示例输出:

Shared resources at \ComputerA  Share name  Type  Used as  Comment  ------------------------------------------------------------------------------- Source      Disk           Source code repository Backup      Disk           Daily backup volume IPC$        IPC            Remote IPC The command completed successfully.

在 Go 中执行并解析:

package main  import (     "bufio"     "fmt"     "os/exec"     "strings" )  func listShares(hostname string) ([]string, error) {     cmd := exec.Command("net", "view", "\\"+hostname)     output, err := cmd.Output()     if err != nil {         return nil, fmt.Errorf("failed to run net view: %w", err)     }      var shares []string     scanner := bufio.NewScanner(strings.NewReader(string(output)))     inShareSection := false      for scanner.Scan() {         line := strings.TrimSpace(scanner.Text())         // 跳过空行和分隔线         if line == "" || strings.HasPrefix(line, "-") || strings.Contains(line, "command completed") {             continue         }         // 找到共享表头后开始采集         if strings.Contains(line, "Share name") {             inShareSection = true             continue         }         if !inShareSection {             continue         }         // 提取第一列(共享名),跳过 IPC$ 和 admin$ 等系统共享(可选)         fields := strings.Fields(line)         if len(fields) > 0 {             shareName := fields[0]             if shareName != "IPC$" && !strings.HasSuffix(shareName, "$") {                 shares = append(shares, shareName)             }         }     }      return shares, scanner.Err() }  func main() {     shares, err := listShares("ComputerA")     if err != nil {         fmt.Printf("Error: %vn", err)         return     }     fmt.Printf("Discovered shares on ComputerA: %vn", shares)     // 示例输出: Discovered shares on ComputerA: [Source Backup] }

⚠️ 注意事项与最佳实践

  • 权限与网络可达性:net view 依赖 SMB 协议通信,要求目标主机启用“网络发现”和“文件和打印机共享”,且当前用户具有访问该计算机共享枚举的权限(通常需属于同一域或配置了正确的本地账户凭据)。
  • 安全性:避免拼接不可信的 hostname 字符串到命令中(防命令注入),建议对输入做白名单校验(如仅允许字母、数字、短横线、点号)。
  • 错误处理:net view 在目标离线、防火墙拦截或 NetBios/SMB 服务未启用时会返回非零退出码,应检查 exec.ExitError 并提供清晰提示。
  • 跨平台限制linux/macOS 无原生 net view;若需跨平台,可考虑调用 smbclient -L //ComputerA -N(需预装 Samba 工具),但需额外处理认证与输出格式差异。
  • 替代方案(进阶):可通过 CGO 调用 Windows API NetShareEnum(syscall 或第三方库如 golang.org/x/sys/windows),但开发复杂度高、维护成本大,一般项目中 net view 已足够稳健。

综上,利用 exec.Command 调用 net view 是当前 Go 生态中发现 Windows 远程共享最简洁、可靠且无需外部依赖的方案。它复用了操作系统原生能力,稳定兼容各 Windows 版本,并易于集成到自动化运维或网络扫描类工具中。

text=ZqhQzanResources