如何在Go中可靠地检测操作系统/平台

120

这是我目前使用的方法,我认为它能够完成任务,但一定有更好的方法:

func isWindows() bool {
    return os.PathSeparator == '\\' && os.PathListSeparator == ';'
}

你可以看到,在我这种情况下,我只需要知道如何检测Windows,但我想知道如何检测任何平台/操作系统的方法。

Play:

http://play.golang.org/p/r4lYWDJDxL


2
当前版本的 Windows 可以使用 Posix 分隔符 '/' 运行良好。你只需要在批处理脚本和旧非 Posix 版本的 Windows 中使用反斜杠。 - Rick-777
你能定义一下“current”吗?我在过去几周中刚刚被硬编码“/'”所坑了。 - Michael Whatcott
3
Windows 95、98、ME以及之前的版本需要使用反斜杠。而Windows 2000、NT以及之后的版本则可以接受斜杠和反斜杠作为等价符号。使用反斜杠可以向下兼容旧版本,而使用斜杠则可满足Posix标准。 - Rick-777
感谢您的澄清,@Rick-777。 - Michael Whatcott
9个回答

195

编译时检测

如果您想根据操作系统有不同的实现,最好在具有该功能实现的文件中添加构建标签,并对每个文件进行标记。标准库中的许多地方都使用了这种方法,例如os包中

这些所谓的“构建约束”或“构建标签”在这里进行了解释。

假设您有常量PATH_SEPARATOR,并希望根据平台而异,则可以创建两个文件,一个用于Windows,另一个用于Unix(除Windows之外的其余平台):

/project/path_windows.go
/project/path_unix.go

这些文件的代码如下:

path_windows.go

// +build windows

package project

const PATH_SEPARATOR = '\\'

path_unix.go

// +build !windows

package project

const PATH_SEPARATOR = '/'

现在您可以在代码中访问PATH_SEPARATOR,并使其依赖于平台。

运行时检测

如果您想在运行时确定操作系统,请使用runtime.GOOS变量:

if runtime.GOOS == "windows" {
    fmt.Println("Hello from Windows")
}

虽然这被编译到运行时中,因此忽略了环境, 但您仍然可以相对确定该值是正确的。 原因是每个值得区分的平台都需要重新编译,因为不同的可执行文件格式, 因此具有新的GOOS值。


1
我尝试运行这个程序,但是出现了一些奇怪的错误。 path_windows.go:4:24: PATH_SEPARATOR在此块中被重新声明。先前的声明在path_unix.go:4:24中。我就是不明白...编译器难道不理解吗? - Ramon J. A. Smit
3
@RamonJ.A.Smit,您需要向这些文件添加构建限制条件,方法如下:在 path_unix.go 文件中应该添加 // +build !windows,而在 path_windows.go 文件中应该添加 // +build windows。我已经编辑了上面的答案以显示它们应该放在哪里。它们必须出现在 package 语句之前,并在后面跟随一行空白,以免被解释为包的文档说明。 - Tim Lewis
谢谢你的额外输入!我会试着去操作一下。谢谢 :-) - Ramon J. A. Smit
@TimLewis,不知道为什么我无法让它工作。我在Windows上,我的data_windows.go中的GetData函数因为data_unix.go中的GetData而被“重新声明”。两者都有适当的构建约束(data_windows.go+build windowsdata_unix.go+build !windows),并且在package语句之前有一个空行,但仍然无法工作。有什么想法吗? - user16442705
1
哦,我明白问题了,我的命令是 go build -o ... cli/*.go,所以它正在构建所有的 Go 文件,而不考虑构建约束。我将其更改为 go build -o ... ./cli,这样就可以工作了。 - user16442705
非常感谢!这对我很有帮助。我将我的包“get_info”分成了3个部分:get_info、get_info_unix和get_info_windows。它起作用了! - Михаил Чеботарев

23

2
GOOS 在运行时包中被设置为编译时的值,并且可以在未安装 Go 工具的情况下读取。 - Tyler Egeto
2
@Matt,你在考虑环境变量。@Tyler在考虑runtime.GOOS,这个方法可以正常工作,例如:http://play.golang.org/p/0dDIEoFXfx - Nick Craig-Wood

10

现在是2023年,针对go 1.18+的正确答案是:

在运行时,您需要:

if runtime.GOOS == "windows" {
  // windows specific code here...
}

如果您需要确定文件系统的路径分隔符
使用:os.PathSeparator 例如:
  • c:\program files
  • /usr/local/bin
如果你需要获取PATH环境变量使用的路径列表分隔符
使用:os.PathListSeparator 例如:
  • /usr/local/bin:/usr/local:
  • "C:\windows";"c:\windows\system32";

2

由于这是一个较旧的问题和答案,我找到了另一种解决方案。

您可以简单地使用os包中定义的常量。该常量返回一个rune,因此您需要进行字符串转换。

string(os.PathSeparator)
string(os.PathListSeparator)

示例:https://play.golang.org/p/g6jnF7W5_pJ

1
这个答案没有展示如何检测平台。问题使用os.PathSeparator和os.PathListSeparator来检测Windows。 - Charlie Tumahai

1

我在Go 1.17.1上进行了测试,确实对我有效。

package main

import (
        "fmt"
        "runtime"
)
func main(){
        fmt.Println(runtime.GOOS)
}

Output:
darwin

1
相同的答案在这里:https://dev59.com/f2Ij5IYBdhLWcg3w04Qo#19847766 - qaisjp

1

我在寻找其他东西时偶然发现了这个,注意到这篇文章的年龄,所以我会添加一个更加更新的补充。如果你只是想处理正确的文件路径,我建议使用filepath.Join()。它可以消除所有操作系统问题的猜测。如果你需要更多的东西,除了文件路径外,使用运行时常量(runtime.GOOSruntime.GOARCH)是最好的方法:playground example


1

使用 path

package main
  
// Importing fmt and path/filepath
import (
    "fmt"
    "path/filepath"
)
  
// Calling main
func main() {
    fmt.Println(filepath.Join("/", "/")) // out: /
    fmt.Println(filepath.Join(""))// out: 
    fmt.Println(filepath.Join("dirname", "dirsubname")) // out: dirname/subdirname
    fmt.Println(filepath.Join(".")) // out: .
  
}

或使用 GOOS

os := runtime.GOOS
    switch os {
    case "windows":
        fmt.Println("Windows")
    case "darwin":
        fmt.Println("MAC operating system")
    case "linux":
        fmt.Println("Linux")
    default:
        fmt.Printf("%s.\n", os)
    }

0

我缺少跨平台的能力。这个链接可能有帮助:https://stackoverflow.com/questions/57038303/detect-operating-system-version - user4466350

0

@nemo 的第一个答案是最合适的,我只想指出,如果您目前是 gopls 语言服务器的用户,则 构建标签 可能无法按预期工作。

目前还没有解决方案或解决方法,您可以做的最多就是更改编辑器的 lsp 配置(vscode、neovim、emacs 等),以选择一个构建标签,以便能够在不出错的情况下编辑带有该标签的文件。 使用其他标签编辑文件将无法正常工作,尝试选择多个标签也会失败。 这是问题 github@go/x/tools/gopls 的当前进展。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接