控制台重定向在应用程序关闭之前不起作用Java

如何解决控制台重定向在应用程序关闭之前不起作用Java

首先,我只是出于娱乐目的而做,无论如何我都不是专业人士。因此,如果我的代码有点草率,我不会感到惊讶!

我正在尝试使用Java 11为控制台应用程序编写GUI包装。我的计划是使用BufferedReader从流程中捕获stdOut和stdErr并将其显示在JTextArea中。在使用命令行参数填充ArrayList之后,从主GUI线程运行此线程。它可以在Ubuntu或Fedora上完美运行,但我在Windows上无法正常使用。当我尝试运行控制台应用程序的交叉编译的Windows版本时,我的应用程序仅在控制台应用程序关闭后才显示其输出。我还尝试用C语言替换一个非常简单的Hello World应用程序(该应用程序通常显示Hello,等待5秒钟,然后显示World),并且执行相同的操作。但是,如果我将ArrayList更改为运行ping.exe -t 8.8.8.8,则效果很好。

我怀疑正在发生的事情是while循环阻塞了线程,但是我不了解它在Linux上的工作方式以及我是否在Windows上使用ping.exe。我也尝试了Redirect stdin and stdout in Java中的代码和ProcessBuilder: Forwarding stdout and stderr of started processes without blocking the main thread中提到的InheritedIO,但是它们也遇到了同样的问题。有什么想法吗?

    public class RunThread extends Thread {
    @Override
    public void run(){
        // Create process with the ArrayList we populated above
        ProcessBuilder pb = new ProcessBuilder(allArgs);
        pb.redirectErrorStream(true);

        // Clear the console
        txtConsoleOutput.setText("");

        // Try to start the process
        try {
            Process p = pb.start();                

            // Get the PID of the process we just started
            pid = p.pid(); 

            // Capture the output
            String cmdOutput;
            BufferedReader inputStream = new BufferedReader(new InputStreamReader(p.getInputStream()));

            // Get stdOut/stdErr of the process and display in the console
            while ((cmdOutput = inputStream.readLine()) != null) {
                txtConsoleOutput.append(cmdOutput + "\n");
            }
            inputStream.close();
        }
        catch (IOException ex) {
            JOptionPane.showMessageDialog(null,"An error (" + ex + ") occurred while attempting to run.",AppName,JOptionPane.ERROR_MESSAGE);
        }

        // Clear the ArrayList so we can run again with a fresh set
        allArgs.clear();

    }
}

更新基于@ControlAltDel提供的代码和@Holger的建议,我将其重写为线程安全的(希望如此!),但最终结果是相同的。>

        SwingWorker <Void,String> RunTV = new SwingWorker <Void,String> () {
        @Override
        protected Void doInBackground() {
            // Create process with the ArrayList we populated above
            ProcessBuilder pb = new ProcessBuilder(allArgs);
            pb.directory(new File(hacktvDirectory));
            pb.redirectErrorStream(true); 
            // Try to start the process
            try {
                Process p = pb.start();                
                // Get the PID of the process we just started
                pid = p.pid();
                // Capture the output
                DataFetcher df = new DataFetcher(p.getInputStream(),new byte[1024],0);
                FetcherListener fl = new FetcherListener() {
                    @Override
                    public void fetchedAll(byte[] bytes) {}
                    @Override
                    public void fetchedMore(byte[] bytes,int start,int end) {
                        publish(new String (bytes,start,end-start));
                    }
                };
                df.addFetcherListener(fl);
                new Thread(df).start();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return null;
        } // End doInBackground

        // Update the GUI from this method.
        @Override
        protected void done() {
            // Revert button to say Run instead of Stop
            changeStopToRun();
            // Clear the ArrayList so we can run again with a fresh set
            allArgs.clear();                
        }
        // Update the GUI from this method.
        @Override
        protected void process(List<String> chunks) {
            // Here we receive the values from publish() and display
            // them in the console
            for (String o : chunks) {
                txtConsoleOutput.append(o);
                txtConsoleOutput.repaint();
            }
        }
    };
    RunTV.execute();
}

更新10/11/2020 在kriegaex发表文章之后,我再次进行了研究。不幸的是,示例代码做了同样的事情,但是它们的注释“例如,如果您的示例程序使用System.out.print()而不是println(),您将在控制台上看不到任何内容,因为输出将被缓冲。” 和我敲钟。

我可以访问要包装的程序的源代码,它是用C语言编写的。它具有以下代码,可将视频分辨率输出到控制台:

void vid_info(vid_t *s)
{
        fprintf(stderr,"Video: %dx%d %.2f fps (full frame %dx%d)\n",s->active_width,s->conf.active_lines,(double) s->conf.frame_rate_num / s->conf.frame_rate_den,s->width,s->conf.lines
        );

        fprintf(stderr,"Sample rate: %d\n",s->sample_rate);
}

如果在第二个fprintf语句下添加 fflush(stderr); ,则可以在控制台上看到这些行,而无需修改自己的代码。我仍然不明白为什么没有它,它就不能在Linux中工作,但是至少我知道答案。

解决方法

Steeviebops:对您的最后一条评论“ ...开始怀疑这是否可能”

就是这样,下面是使用可读取的回调读取器DataFetcher进行操作的方法:(您可能还想使用System.err)(https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/io/

  DataFetcher df = new DataFetcher(System.in,new byte[1024],0);
  FetcherListener fl = new FetcherListener() {
    public void gotAll(byte[] bytes) {}
    public void getMore(byte[] bytes,int start,int end) {
      SwingUtilities.invokeLater(new Runnable() {
         txtConsoleOutput.append(new String (bytes,start,end-start) + "\n");
         // You may or may not need/want to do a repaint here
      });
    }
  }
  df.addFetcherListener(fl);
  new Thread(df).start();
,

我个人评论的相关信息:

另一个想法:您是否尝试过在不使用Swing的情况下重现它,而只是将从流中读取的内容转储到程序的文本控制台中?也许它与ping一起工作但对其他测试程序却不起作用的问题是,后者仅写入缓冲的流,该缓冲的流仅偶尔刷新一次(例如,退出时),因此没有任何要读取的内容。您自己的程序。我想如果将“ Hello” +“ world”写入缓冲区明显大于那些短字符串的流中,可能会导致这种行为。 ping不过可以直接写入并刷新。

例如,如果您的示例程序使用System.out.print()而不是println(),您将在控制台上看不到任何内容,因为输出将被缓冲。仅在插入println()(暗示对BufferedWriter.flushBuffer()的调用)或直接刷新编写器的缓冲区之后,从第一个进程的控制台读取的其他程序才可以读取某些内容。

目标应用程序,写入控制台:

import java.util.Random;

public class TargetApp {
  public static void main(String[] args) throws InterruptedException {
    System.out.print("Hello ");
    Thread.sleep(1500);
    System.out.println("world!");
    Random random = new Random();
    for (int i = 0; i < 250; i++) {
      System.out.print("#");
      if (random.nextInt(20) == 0)
        System.out.println();
      Thread.sleep(50);
    }
  }
}

控制器应用程序,读取目标应用程序的控制台输出:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

public class ControllerApp extends Thread {
  List<String> allArgs = Arrays.asList(
    //"ping","-n","3","google.de"
    "java","-cp","bin","TargetApp"
  );

  @Override
  public void run() {
    try (
      BufferedReader inputStream = new BufferedReader(
        new InputStreamReader(
          new ProcessBuilder(allArgs)
            .redirectErrorStream(true)
            .start()
            .getInputStream()
        )
      )
    ) {
      String cmdOutput;
      while ((cmdOutput = inputStream.readLine()) != null) {
        System.out.println(cmdOutput);
      }
    }
    catch (IOException ex) {
      throw new RuntimeException(ex);
    }
  }

  public static void main(String[] args) throws InterruptedException {
    new ControllerApp().start();
  }
}

如果运行ControllerApp,将会看到它没有显示“ Hello”和“ world!”。分开但一次。 “#”字符串也是如此。它根据另一个程序的缓冲区刷新行为按块读取它们。您还将注意到,它以停停的方式编写以下协议,而不是每50毫秒连续发送“#”字符。

Hello world!
#####
#################
############
####
#############
##########################################
###############
################
#################
######
##########
######
#######
############################################
#########
#################
##########

因此,如果这是您的问题,那么它是在TargetApp中而不是ControllerApp中。那么它也将与Swing无关。


更新:我忘了提到您可以通过注释掉这两行来模拟只有在TargetApp退出后才能看到最后输出的行为:

//      if (random.nextInt(20) == 0)
//        System.out.println();

然后,控制台日志如下所示,仅当TargetApp终止时才打印长的“#”行:

Hello world!
##########################################################################################################################################################################################################################################################

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-