[英]How can I check if a Java program's input/output streams are connected to a terminal?
I would like a Java program to have different default settings (verbosity, possibly colored output where supported) depending on its use.我希望 Java 程序根据其用途具有不同的默认设置(详细程度,可能支持彩色输出)。 In C, there is an isatty() function which will return 1 if a file descriptor is connected to a terminal, and 0 otherwise.
在 C 中,有一个 isatty() 函数,如果文件描述符连接到终端,它将返回 1,否则返回 0。 Is there an equivalent for this in Java?
在 Java 中是否有这样的等价物? I haven't seen anything in the JavaDoc for InputStream or PrintStream.
我在 JavaDoc 中没有看到 InputStream 或 PrintStream 的任何内容。
System.console(), as already mentioned by @Bombe, works for simple use cases of checking console-connectedness. System.console(),正如@Bombe 已经提到的,适用于检查控制台连接的简单用例。 The problem with System.console() however, is that it doesn't let you determine whether it's STDIN or STDOUT (or both or neither) that is connected to a console.
然而,System.console() 的问题在于它不能让您确定连接到控制台的是 STDIN 还是 STDOUT(或两者都不是)。
The difference between Java's System.console() and C's isatty() can be illustrated in the following case-breakdown (where we pipe data to/from a hypothetical Foo.class): Java 的System.console()和 C 的isatty()之间的区别可以在以下案例分解中说明(我们将数据传入/传出假设的 Foo.class):
1) STDIN and STDOUT are tty 1) STDIN 和 STDOUT 是 tty
%> java Foo
System.console() => <Console instance>
isatty(STDIN_FILENO) => 1
isatty(STDOUT_FILENO) => 1
2) STDOUT is tty 2) STDOUT 是 tty
%> echo foo | java Foo
System.console() => null
isatty(STDIN_FILENO) => 0
isatty(STDOUT_FILENO) => 1
3) STDIN is tty 3) STDIN 是 tty
%> java Foo | cat
System.console() => null
isatty(STDIN_FILENO) => 1
isatty(STDOUT_FILENO) => 0
4) Neither STDIN nor STDOUT are tty 4) STDIN 和 STDOUT 都不是 tty
%> echo foo | java Foo | cat
System.console() => null
isatty(STDIN_FILENO) => 0
isatty(STDOUT_FILENO) => 0
I can't tell you why Java doesn't support better tty-checking.我无法告诉您为什么 Java 不支持更好的 tty 检查。 I wonder if some of Java's target OS's don't support it.
我想知道某些 Java 的目标操作系统是否不支持它。
It technically is possible to do this in Java (as stephen-c@ pointed out) with some fairly simple JNI , but it will make your application dependent on C-code that may not be portable to other systems.从技术上讲,可以使用一些相当简单的 JNI在 Java 中执行此操作(如 stephen-c@ 指出的那样),但它会使您的应用程序依赖于可能无法移植到其他系统的 C 代码。 I can understand that some people may not want to go there.
我能理解有些人可能不想去那里。
A quick example of what the JNI would look like (glossing over a lot of details): JNI 外观的快速示例(掩盖了很多细节):
Java: tty/TtyUtils.java 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 (assumes matching ttyutils.h ), compiled to libttyutils.so 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;
}
If you have the option of using another language, most other languages I can think of support tty-checking.如果您可以选择使用另一种语言,我能想到的大多数其他语言都支持 tty-checking。 But, since you asked the question, you probably already know that.
但是,既然你问了这个问题,你可能已经知道了。 The first that come to mind for me (aside from C/C++) are Ruby , Python , Golang and Perl .
我首先想到的(除了 C/C++)是Ruby 、 Python 、 Golang和Perl 。
System.console() will return the console your application is connected to if it is connected, otherwise it returns null
. System.console()将返回您的应用程序连接到的控制台,否则返回
null
。 (Note that it's only available from JDK 6 on.) (请注意,它仅从 JDK 6 开始可用。)
The short answer is that there is no direct equivalent of 'isatty' in standard Java.简短的回答是,在标准 Java 中没有直接等价于 'isatty' 的东西。 There's been a RFE for something like this in the Java Bug Database since 1997, but it only
has
had 1 one measly vote.还有的是一个RFE用于在Java错误数据库这样的事情,因为1997年,但它只是
有
过1一个可怜的票。
In theory, you might be able to implement 'isatty' using JNI magic.理论上,您可以使用 JNI 魔法实现“isatty”。 But that introduces all sorts of potential problems.
但这会带来各种潜在的问题。 I wouldn't even contemplate doing this myself ...
我什至不会考虑自己做这个......
1 - Voting for Java bugs to be fixed went away around the time that Oracle took over Sun. 1 - 在 Oracle 接管 Sun 的时候,对修复 Java 错误的投票停止了。
If you don't want to compile C source yourself, you can use the Jansi library.如果不想自己编译 C 源代码,可以使用 Jansi 库。 It's a lot smaller than jnr-posix
它比 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) );
There is another way.还有另一种方法。 I discovered this by chance as I needed to use
/dev/tty
.我偶然发现了这一点,因为我需要使用
/dev/tty
。 I noticed a FileSystemException
is raised, when the Java program tries to create an InputStream
from the tty device file if the program is not part of a TTY, like Gradle daemons.我注意到一个
FileSystemException
被引发,当 Java 程序试图从 tty 设备文件创建一个InputStream
,如果程序不是 TTY 的一部分,比如 Gradle 守护进程。 However if any of the stdin, stdout, or stderr is connected to a terminal, this code won't raise an exception with the message:但是,如果 stdin、stdout 或 stderr 中的任何一个连接到终端,则此代码不会引发带有消息的异常:
(Device not configured)
(Device not configured)
No such device or address
No such device or address
Unfortunately checking if /dev/tty
exists and is readable will be true.不幸的是,检查
/dev/tty
存在并且可读将是真的。 This FSE only happens when actually trying to read from the file, without reading it.此 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";
}
While this approach is ugly, it avoids the use of third party libraries.虽然这种方法很丑陋,但它避免了使用第三方库。 This doesn't answer the question which of the standard stream is connected to a terminal though, for that it might me better to rely on a terminal library, like Jansi or JLine 3.
这并没有回答哪个标准流连接到终端的问题,因为我最好依赖终端库,如 Jansi 或 JLine 3。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.