有没有一种方法可以找到Java程序运行的控制台的宽度?
如果可能的话,我希望这是跨平台的...
我不想改变缓冲区或窗口的宽度,我只想知道它的宽度,以便正确格式化正在打印到屏幕上的文本。
没有可靠的跨平台解决这个问题的方案。实际上,有些情况下无法知道真正的控制台宽度。
(查看其他答案以了解在某些情况和/或某些平台上有效的方法。但要注意这些方法的限制...)
例如,在Linux系统上,通常可以从LINES和COLUMNS环境变量中找到理论终端尺寸。虽然这些变量在调整某些“终端仿真器”窗口大小时会自动更新,但并非总是如此。实际上,在通过telnet协议连接的远程控制台的情况下,无法将实际终端尺寸传递给用户的shell。
编辑:只是为了补充说明,如果用户在启动Java应用程序后更改了Linux上xterm的尺寸,则Java应用程序不会被通知,并且它不会在其LINES
和COLUMNS
环境变量的副本中看到新的尺寸!
编辑2:我的错误: LINES
和 COLUMNS
是bash
shell变量,默认情况下不导出到环境中。您可以在运行Java应用程序之前运行export COLUMNS LINES
来“修复”这个问题。
eval \
resize`; export COLUMNS`来更改终端的大小。 - Ogre Psalm33实际上,在Java中已经存在一个Java库可以做到这一点:JLine 2。(在SourceForce上有一个旧版本,但是该链接到GitHub似乎是最新的。)
我在Linux(Mint 14)和Windows(我不知道是哪个版本)下使用JLine 2.11测试过这个方法:
terminalWidth = jline.TerminalFactory.get().getWidth();
JLine承诺在Mac上也能工作。
我发现它在Eclipse控制台下返回错误的宽度(比如1
!),甚至Java的本地控制台也不能在Eclipse下工作!而且,不幸的是,在Cygwin下也有这个问题。但是我写了一些代码来检查不合理的值(< 10
),并在这些情况下只使用80。
JLine 3的更新(根据Mark提供的信息——谢谢你,老兄!):
terminalWidth = org.jline.terminal.TerminalBuilder.terminal().getWidth()
TerminalLineSetting
中调用 stty -a
,而在Windows上,它通过org.fusesource.jansi.internal.WindowsSupport
钩入了JAnsi库中的封闭源代码部分。 - dimo414Terminal
的开销? - Garret Wilson有一个技巧,基于ANSI转义代码。它们不提供直接查询控制台大小的方式,但它们有一个请求当前光标位置的命令。通过将光标移动到一个非常高的行和列,然后请求光标位置,可以获得准确的测量值。
将此与存储/恢复光标位置的命令结合使用,例如以下示例:
发送以下序列到终端(stdout)
"\u001b[s" // save cursor position
"\u001b[5000;5000H" // move to col 5000 row 5000
"\u001b[6n" // request cursor position
"\u001b[u" // restore cursor position
现在观察标准输入,你应该收到一个看起来像\u001b[25;80R
的序列,其中25是行数,80是列数。
我最初在Lanterna库中看到它被使用。
更新:
实际上有四种不同的方法可以实现此目的,但它们都对程序运行的环境或终端设备/模拟器做出了某些假设。
cmd.exe
,这可能不起作用。tput
进行查询。操作系统相关,并假设您连接到TTY设备。例如,无法在ssh上工作。COLUMNS
/ROWS
变量的解决方案所做的。远非普遍存在,但如果您为应用程序提供确保必要的环境变量被导出的包装脚本,它可能非常有效。stdin
和 stdout
连接到终端时有效。但是,它假定它总是有效的。用户可能会将输出导入文件中,这将打破该假设。在这种情况下,转义代码将被写入文件中。应用程序在使用此技术之前应检查是否连接到终端。不幸的是,我怀疑没有标准的Java API来检查。在C中,可能类似于 isatty。 - JojOatXGMESystem.in
读取。 - Björn Zurmaar编辑:请参考@dave_thompson_085的评论,因为那很可能是更好的方法,使用ProcessBuilder
。
另一个答案提到在脚本中运行tput cols
,然后再启动命令。但是如果你想在Java已经启动后使用Runtime.getRuntime().exec()
来运行它,你会发现tput无法与你的终端通信,因为Java已经重定向了stdout和stderr。作为一种不太可移植的解决方法,你可以使用神奇的/dev/tty
设备,它指的是当前进程的终端。这样你就可以运行像这样的东西:
Process p = Runtime.getRuntime().exec(new String[] {
"bash", "-c", "tput cols 2> /dev/tty" });
// Read the output of this process to get your terminal width
这对我在Linux上有效,但我不指望它能在任何地方都有效。希望它能在Mac上工作。但它肯定不会在Windows上工作,尽管在Cygwin上可能会。
ProcessBuilder
可以将Java自己的stdin/out/err传递给子进程,而不是默认的重定向管道;请参见inheritIO()
和redirect*(Redirect.INHERIT)
--如果用户已经重定向了java
本身,这并没有什么帮助,但在这种情况下,为什么要关心你不使用的I/O设备的宽度呢? - dave_thompson_085我之前一直在解决这个问题。我使用了几种不同的技术。但是要实现真正的跨平台解决方案很困难。
我尝试过像这样的方法:
String os = System.getProperty("os.name").toLowerCase();
//Windows
if(os.contains("win")){
System.out.append("Windows Detected");
//set Windows Dos Terminal width 80, height 25
Process p = Runtime.getRuntime().exec("mode 80, 25");
}
//Mac
if(os.contains("mac")){
System.out.println("Macintosh Detected");
//... I dont know...try Google
}
//Linux
if(os.contains("linux")){
System.out.println("Linux Detected");
try {
ProcessBuilder pb = new ProcessBuilder("bash","-c","echo $COLUMNS >/home/$USER/bin/temp.txt" );
pb.start();
}catch (Exception e) {
System.out.println("exception happened - here's what I know: ");
e.printStackTrace();
System.exit(-1);
}
}
//#!/bin/bash
//#clear the screen
clear
//#get the console width and height
c=$[$(tput cols)]
l=$[$(tput lines)]
//#pass the columns, lines and an optional third value as String[] args.
java -jar ~/bin/Plus.jar $c $l $1
export COLUMNS
,则可以简单地使用System.getenv("COLUMNS")
;无需使用ProcessBuilder
启动子进程。 - dimo414对我来说,了解终端窗口(当窗口大小改变时仍然不正确)的唯一方法是使用类似于以下命令的命令:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "mode con");
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
cmd.exe
部分时,会显示找不到命令。还要注意redirectError
部分。如果不使用它,则将使用Java输出大小而不是实际大小。只有使用这种组合才能获取实际大小。Python似乎有一个很好的解决方案:查询输出终端的大小11.9.3。我不会指望这个功能在Java核心中可用,但你可以使用Jython来使Python的功能可用。
shutil
文档所提到的,这会回退到os.get_terminal_size()
,它是特定于操作系统并且是用C语言实现的。使用Jython可能适用于某些用例,但这是一个相对较重的工具。 - dimo414
tput rmam
禁用换行和tput smam
启用换行(https://itectec.com/askdifferent/macos-disable-line-wrapping-for-output-in-the-terminal/)。可以直接在控制台中执行,或者作为从 Java 直接执行的 bash 命令。 - user1300214