如何在Java中检测类Unix操作系统?

35

好的,我知道System.getProperty("os.name")会给出我正在运行的操作系统的名称,但这并没有太多帮助。我需要知道的是我所运行的操作系统是否是“类Unix”操作系统,我不关心它是HP-UX、AIX、Mac OS X或其他什么操作系统。

可能的 os.name 值列表中看来,检测“类Unix”操作系统的一种快速且简单的方法是检查 os.name 是否不包含“Windows”。这将给出一些误报,但我的代码很少会遇到这些操作系统!尽管如此,如果有更好的方法,我还是想知道。


4
你为何要寻找“类Unix系统”?如果你是在寻找某个特定的功能,最好直接查找该功能。 - David Thornley
5
是的,通常可以用原始的Java方法规避这种情况,但有时候这样做并不值得麻烦。但是,确实值得麻烦。如果使用Java.io.file.list(http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/io/File.html#list()),您可以获得目录列表,并避免调用ls或dir以及避免操作系统检测逻辑。使用与操作系统无关的API调用总比玩弄操作系统检测逻辑更好。 - Freiheit
1
Java不需要那个功能 ;) - Brendan Long
1
只是额外提醒一下,尝试特定命令也可能没有帮助(取决于您最终使用的机器)-- uname、ls等在我的Windows机器上都可以工作,因为我安装了MinGW+MSYS... - Jonathan
6
我希望能够检测当前操作系统是否为类Unix系统,原因是我正在创建目录,当在Unix系统上运行时,需要使用"chmod"命令将所有人的写权限打开。但在Windows上运行时,我不想(也不需要)调用"chmod"命令。据我所知,Java没有一个独立于操作系统的API来更改文件权限。 - mluisbrown
显示剩余3条评论
9个回答

35

使用Commons Lang中的org.apache.commons.lang.SystemUtils实用类,它有一个很好的IS_OS_UNIX常量。以下是来自javadoc的描述:

如果这是一个符合POSIX的系统,例如AIX、HP-UX、Irix、Linux、MacOSX、Solaris或SUN OS,则为 true

如果OS_NAME为空,则此字段将返回false。

测试代码如下:

if (SystemUtils.IS_OS_UNIX) {
    ...
}

简单、有效、易于阅读,没有神秘的技巧。


@Anders:首先,让我强调一点,我们正在谈论javadoc。在声称它“错误”之前,您有没有检查过实现?其次,列表中有什么“完全错误”的地方,您会添加什么?最后,请随时提交一个javadoc补丁,如果它能改进某些内容,那么补丁通常是受欢迎的。 - Pascal Thivent
上次我看的时候,有很多不同的Linux发行版,即使是最古老的(Slackware)也不符合POSIX标准,说实话我不相信任何一个发行版能够声称自己符合。Solaris和SUN OS也不完全符合(现在应该是一样的),MacOSX也不符合POSIX标准。而且,我没有检查过实现。我会修改第一个评论,希望实现比文档更好。 - Anders
@Anders:感谢您的反馈和澄清,我明白您的观点了(现在我同意,javadoc不太好)。 - Pascal Thivent
确实是个好主意,所以我点了赞。但是使用 SystemUtils.IS_OS_WINDOWS 不是更简单吗? - saygley

22

我已经在Windows XP、Vista、Win7、Mac OS 10.3-10.6以及各种Linux发行版上使用了您的方案,没有出现任何问题:

    if (System.getProperty("os.name").startsWith("Windows")) {
        // includes: Windows 2000,  Windows 95, Windows 98, Windows NT, Windows Vista, Windows XP
    } else {
        // everything else
    } 

基本上,通过检测 Windows 来检测 类 Unix


尽管有很多好的想法(我特别喜欢File.listRoots),但我认为这是做到这一点的“正确”方式。误报的可能性非常小。 - mluisbrown
如果有人使用类似ReactOS的操作系统,这个方法不会起作用,对吧?它确实不像Unix,但也不是Windows。 - Aaron Esau
@Arin。同意。八年前的答案已经过时了。我会将其设为社区维基并让大家参与编辑。 - David J. Liszewski

19

File.listRoots()会返回文件系统根目录的数组。

如果您正在Unix类系统上,则该数组应包含单个条目"/",而在Windows系统上,您将得到类似于["C:", "D:", ...]的内容。

编辑: @chris_l: 我完全忘记了移动电话。一些挖掘结果表明,Android返回"/\0\0" - 一个斜杠后跟两个空字节(被认为是错误)。看起来我们通过运气和巧合避免了误报。不幸的是,找不到其他手机的好数据。

在桌面和移动电话上运行相同的代码可能并不是一个好主意,但知道这点很有趣。看起来这取决于需要检查特定功能而不仅仅是系统类型。


2
我已经写了多年的Java,从来没有注意到那个函数的存在。太好了! - Brent Writes Code
非常好的想法,但我想知道,是否还有其他非 Windows、非 Unix 系统拥有根目录“/”? - Chris Lercher
@chris_l:大多数非Windows、非Unix系统都是为特定目的和特定语言而建立的研究项目,例如Amoeba(Python)和House(Haskell)。虽然这听起来像是一个回避问题的答案,但我猜想它们中的大多数都没有内置的Java虚拟机。除此之外,我必须同意David Thornlay在问题上关于测试特定功能而不仅仅是类Unix的评论。 - MikeD
1
我更多地考虑支持Java的手机 - 我不确定它们的系统根目录是什么。 - Chris Lercher

10

根据Javadoc文档,UNIX系统中此字段的值为'/';而在Microsoft Windows系统中它是'\'

System.out.println( File.separatorChar == '/' ? "Unix" : "Windows" );

比listRoots更有效率。 - sje397
1
我经常这样做。这似乎有点无聊,应该有更明确的方法,但它能够工作。 - Jay
1
经典(OSX之前)的Macintosh怎么样?分隔符是“:”吗? - Steven Schlansker
@Steven:是否有适用于OSX之前的Macintosh的现代JVM? - Jesper
2
@ Jesper:几乎可以肯定不会有其他分隔符在使用,但这并不是重点。你确定没有其他分隔符在使用,更重要的是,将来永远不会有吗?像这样做事情充满了危险,你将来只会遇到问题。 - Steven Schlansker
@Steven - 对于这种情况,可以查看行分隔符,它在 Mac 上仅为回车符,Unix 上为换行符,而在 Dos/Windows 上则是两者都有。 - stacker

6

System.getProperty("os.name"); 是您能得到的最好的。

(注:该代码用于获取操作系统名称)

1

我同意@Fuzzy的观点,认为Java想让你获取那些信息的唯一方式是通过os.name属性。

我能想到的其他方法只有:

  1. 编写一个shell脚本或批处理文件来启动你的Java应用程序,并使用JVM的-D参数传递操作系统信息。虽然根据你的描述,这似乎不可行。

  2. 尝试检查特定于操作系统的目录是否存在。例如,你可以假设Unix-like系统上始终存在目录"/",但在Windows上不存在,然后像这样执行:

    if((new File("/")).exists()) { System.out.println("我在Unix系统上!"); }

  3. 尝试启动一个特定于Unix的命令行命令,如ls,并检查返回代码。如果成功了,你就在Unix-like系统上,否则你就在Windows上。

所有这些解决方案都只是hack,而且坦率地说,我对它们中的任何一个都不太满意。不幸的是,你最好还是按照最初的想法去做。很有趣,对吧?


2
很遗憾,你的第二个解决方案不起作用。即使在Windows系统上,Java仍然会返回"I'm on a Unix system!"。 - Jaime Garcia
呃,感谢@Ferrari提醒。 - Brent Writes Code

1

使用 File.pathSeparator 或 File.separator。在 Windows 中,前者将返回“;”,在 Unix 中将返回“:”。后者将返回“\”(反斜杠)在 Windows 中,在 Unix 中将返回“/”(正斜杠)。


-1
你可以尝试执行uname命令 - 这个命令应该在所有类Unix系统上都可用。

-2
package com.appspot.x19290;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class UnixCheck {
    public static void main(String[] args) {
        UnixCheck s = UnixCheck.S;
        String isUnix = s.unix ? "is Unix" : "not Unix";
        try {
            System.out.println(isUnix + ", devnull: " + s.devnull.getPath());
        } catch (NullPointerException e) {
            System.out.println(isUnix + ", devnull: unknown");
        }
    }

    public static final UnixCheck S = new UnixCheck();
    public static final UnixCheck TEST = new UnixCheck(true);

    public final boolean unix;
    public final File devnull;

    private UnixCheck() {
        this(false);
    }

    private UnixCheck(boolean testing) {
        String path;
        path = testing ? "/<dev>/<no><such><null><device>" : "/dev/null";
        File devnull = devnullOrNone(path);
        if (devnull == null) {
            this.unix = false;
            path = testing ? "<no><such><null><device>" : "nul";
            this.devnull = devnullOrNone(path);
        } else {
            this.unix = true;
            this.devnull = devnull;
        }
    }

    private static File devnullOrNone(String name) {
        File file = new File(name);
        if (file.isFile())
            return null;
        if (file.isDirectory())
            return null;
        try {
            FileInputStream i = new FileInputStream(file);
            try {
                i.read();
            } finally {
                i.close();
            }
        } catch (IOException e) {
            return null;
        }
        return file;
    }
}

1
这不是一个好的检查方式,因为在Windows平台上可能会有一个C:/dev/null目录...虽然这种情况并不常见,但如果发现程序在这种情况下没有正确的行为,那么可能会很麻烦!!最好检查'os.name'属性。 - Donatello

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