如何在EOF之后重新打开System.in或完全阻止EOF?

时间:2017-06-10 20:32:19

标签: java unix

我确定已经被问到/已经回答了,我只是无法找出用于找到答案的搜索词...

我有一个用Java编写的命令行工具,在UNIX上运行,但在某些时候可能是Windows。该程序使用System.inBufferedReader.readline()读取用户输入。用户输入^D(EOF)而非^U或其他任何内容,System.in关闭,我们都非常不满意。我需要通过禁用它,捕获和忽略它,或者能够重新打开System.in(可能是在UNIX上查找并打开tty,但在Windows上如何?)来生存EOF。我希望透明地执行此操作(用户看不到),但由于这可能是从脚本运行的,因此我可以使用UNIX(Windows / .bat)命令。

提前致谢。

3 个答案:

答案 0 :(得分:0)

您可以使用内部API来捕获信号:

import sun.misc.Signal;
import sun.misc.SignalHandler;
public class NoStop {
    public static void main(final String[] args) {
        SignalHandler ignoreHandler = sig -> System.out.println("Ignoring signal " + sig);
        Signal.handle(new Signal("INT"), ignoreHandler);

jnr-posix

import jnr.posix.POSIX;
import jnr.posix.POSIXFactory;
import jnr.posix.SignalHandler;
public class NoStop {
    public static void main(final String[] args) {
        SignalHandler ignoreHandler = sig -> System.out.println("Ignoring signal " + sig);
        POSIX posix = POSIXFactory.getPOSIX();
        posix.signal(Signal.SIGINT, ignoreHandler);

这样,键入^C无效,键入^D会导致从null读取System.in字符串,您可以在应用程序逻辑中处理。

第一个是使用内部API,编译器抱怨:

  

Signal是内部专有API,可能会在将来的版本中删除

但它可以派上用场来解决您的问题,sometimes it's OK。在你的情况下,划分到一个微小的范围是微不足道的,并找到一个替代如果 API实际上消失了。如果必须的话,我会在这种特殊情况下选择内部API(因为jnr-posix带来的是过度杀伤,但值得一提)。

在Mac& Linux,Java 8& 9。

如果您不喜欢编译器警告,请使用反射(*)调用方法。

或者,您可以使用命令行包装器启动java程序,该命令行包装器将为您拦截信号(您必须在C中开发,this answer显示如何)。 (我检查了rlwrap是否这样做,我有点失望但是没有。)

(*)(不是说它更好,只是觉得它可能是一个有用的例子)

private static void trapSignalsUsingReflection() {
    try {
        Class signalClass = Class.forName("sun.misc.Signal");
        Class signalHandlerInterface = Class.forName("sun.misc.SignalHandler");
        Object signal = signalClass.getDeclaredConstructor(String.class).newInstance("INT");
        Object handler = Proxy.newProxyInstance(
                NoStop.class.getClassLoader(),
                new Class[]{signalHandlerInterface},
                (proxy, method, args) -> {
                    System.out.println("Ignoring signal: " + args[0]);
                    return null;
                });
        Method handleMethod = signalClass.getMethod("handle", signalClass, signalHandlerInterface);
        handleMethod.invoke(null, signal, handler);
    }
    catch(ClassNotFoundException|IllegalAccessException|InstantiationException|NoSuchMethodException|InvocationTargetException e) {
        throw new RuntimeException("Oops, internal API has changed, quick," +
                "go to https://stackoverflow.com/a/34188676/8022004 & tell dimo414 he was wrong", e);
    }
}

答案 1 :(得分:-1)

请尝试使用我在Ubuntu 16.04 LTS上测试的以下代码

public static void main(final String[] args) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String str = "";
    final StringBuilder message = new StringBuilder();
    boolean stopFlag = false;
    while (!stopFlag) {
        str = reader.readLine();
        if (str == null) {
            // need to reset stream and skip Exception
            System.in.reset();
            reader = new BufferedReader(new InputStreamReader(System.in));
            continue;
        }
        // some stop condition
        else if ("stop".equalsIgnoreCase(str)) {
            stopFlag = true;
            continue;
        }
        message.append(str);
    }
    System.out.println(message.toString());
}

Ctrl + D或Ctrl + C不会停止应用

答案 2 :(得分:-1)

  

如何在EOF之后重新打开System.in?

在读取流结束后,

System.in未关闭。你可以继续阅读它(并继续得到流的结束)。这里没有问题需要解决。

除非你关闭它。解决方案:不要。

  

还是完全阻止EOF?

你不能。