我希望Java程序具有不同的默认设置(详细程度,可能是支持的彩色输出),具体取决于其用途。在C中,有一个isatty()函数,如果文件描述符连接到终端,则返回1,否则返回0。在Java中是否有相同的功能?我没有在JavaDoc中看到任何有关InputStream或PrintStream的内容。
答案 0 :(得分:33)
@Bombe已经提到的System.console()适用于检查控制台连接性的简单用例。但是,System.console()的问题在于,它不能让您确定它是连接到控制台的STDIN还是STDOUT(或两者都没有)。
Java' System.console()和C' isatty()之间的区别可以在以下案例细分中说明(我们将数据传入/传出假设的Foo.class) :
1)STDIN和STDOUT是tty
%> java Foo
System.console() => <Console instance>
isatty(STDIN_FILENO) => 1
isatty(STDOUT_FILENO) => 1
2)STDOUT是tty
%> echo foo | java Foo
System.console() => null
isatty(STDIN_FILENO) => 0
isatty(STDOUT_FILENO) => 1
3)STDIN是tty
%> java Foo | cat
System.console() => null
isatty(STDIN_FILENO) => 1
isatty(STDOUT_FILENO) => 0
4)STDIN和STDOUT都不是tty
%> echo foo | java Foo | cat
System.console() => null
isatty(STDIN_FILENO) => 0
isatty(STDOUT_FILENO) => 0
我无法告诉你为什么Java不支持更好的tty-checking。我想知道Java的某些目标操作系统是否不支持它。
技术上 可以在Java中执行此操作(如stephen-c @指出)和fairly simple JNI,但它会使您的应用程序依赖于可能没有的C代码可移植到其他系统。我可以理解有些人可能不想去那里。
JNI的一个简单例子(掩盖了很多细节):
Java: tty / TtyUtils.java
public class TtyUtils {
static {
System.loadLibrary("ttyutils");
}
// FileDescriptor 0 for STDIN, 1 for STDOUT
public native static boolean isTty(int fileDescriptor);
}
C: ttyutils.c (假设匹配 ttyutils.h ),编译为 libttyutils.so
#include <jni.h>
#include <unistd.h>
JNIEXPORT jboolean JNICALL Java_tty_TtyUtils_isTty
(JNIEnv *env, jclass cls, jint fileDescriptor) {
return isatty(fileDescriptor)? JNI_TRUE: JNI_FALSE;
}
如果您可以选择使用其他语言,我可以考虑支持tty检查的大多数其他语言。但是,既然你问了这个问题,你可能已经知道了。我想到的第一个问题(除了C / C ++之外)是Ruby,Python,Golang和Perl。
答案 1 :(得分:25)
System.console()将返回应用程序连接的控制台(如果已连接),否则返回null
。 (请注意,它仅在JDK 6上可用。)
答案 2 :(得分:8)
简短的回答是标准Java中没有“isatty”的直接等价物。自1997年以来,Java Bug数据库中出现了类似这样的RFE,但只有 才有 1 一次投票。
理论上,您可以使用JNI魔法实现'isatty'。但这引入了各种潜在的问题。我甚至不打算自己这样做......
1 - 在Oracle接管Sun的时候,Java错误的修复就消失了。
答案 3 :(得分:6)
您可以使用jnr-posix库从Java调用本机posix方法:
import jnr.posix.POSIX;
import jnr.posix.POSIXFactory;
import java.io.FileDescriptor;
POSIX posix = POSIXFactory.getPOSIX();
posix.isatty(FileDescriptor.out);
答案 4 :(得分:2)
如果您不想自己编译C源代码,可以使用Jansi库。它比jnr-posix小很多
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>1.17.1</version>
</dependency>
...
import static org.fusesource.jansi.internal.CLibrary.isatty;
...
System.out.println( isatty(STDIN_FILENO) );
答案 5 :(得分:0)
还有一种方法。我偶然发现了这一点,因为我需要使用 /dev/tty
。我注意到 FileSystemException
被引发,当 Java 程序尝试从 tty 设备文件创建 InputStream
时,如果程序不是 TTY 的一部分,如 Gradle 守护程序。但是,如果 stdin、stdout 或 stderr 中的任何一个连接到终端,则此代码不会引发带有消息的异常:
(Device not configured)
上No such device or address
不幸的是,检查 /dev/tty
是否存在且可读将是真的。此 FSE 仅在实际尝试从文件中读取时发生,而不是读取它。
// straw man check to identify if this is running in a terminal
// System.console() requires that both stdin and stdout are connected to a terminal
// which is not always the case (eg with pipes).
// However, it happens that trying to read from /dev/tty works
// when the application is connected to a terminal, and fails when not
// with the message
// on macOS '(Device not configured)'
// on Linux 'No such device or address'
//
// Unfortunately Files::notExists or Files::isReadable don't fail.
//noinspection EmptyTryBlock
try (var ignored = Files.newInputStream(Path.of("/dev/tty"))) {
return "in a tty"
} catch (FileSystemException fileSystemException) {
return "not in a tty";
}
虽然这种方法很丑陋,但它避免了使用第三方库。这并没有回答哪个标准流连接到终端的问题,因为我最好依赖终端库,如 Jansi 或 JLine 3。