【问题标题】:Execute external program using ProcessBuilder and provide input使用 ProcessBuilder 执行外部程序并提供输入
【发布时间】:2010-07-13 19:27:48
【问题描述】:

我正在尝试使用 ProcessBuilder 使用 Java 执行外部程序,但它需要用户输入。

更具体地说,程序是PGSQL (Postgres SQL),当它执行时,程序会提示用户输入密码。绕过它的唯一方法是将包含密码的文件保存在用户主页中,我试图避免这种情况,所以我想从 Java 执行程序并使用进程的输出流发送密码。

当程序不需要任何用户输入时,代码工作正常,但是当我从用户主页删除密码文件时,程序挂起。我看到它正在执行,但没有任何反应。如果我调试它,它会到达一段时间,然后在我终止进程之前什么都不会发生。

这是代码,任何帮助将不胜感激。

@Test
public void testSQLExecution() throws Exception {
String path = "C:/tmp";
List<String> commandList = new ArrayList<String>();
commandList.add("psql");
commandList.add("-f");
commandList.add("test.sql");
commandList.add("-h");
commandList.add(HOST);
commandList.add("-p");
commandList.add(PORT);
commandList.add("-U");
commandList.add(DATABASE);
commandList.add(SCHEMA);

ProcessBuilder processBuilder = new ProcessBuilder(commandList);
processBuilder.directory(new File(path));
processBuilder.redirectErrorStream(true);

Process p = processBuilder.start();

String line;
BufferedReader input = new BufferedReader(new InputStreamReader(p
    .getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(p
    .getOutputStream()));

out.write("password");
out.newLine();
out.flush();
out.close();

    // When this line is reached, the execution halts.
while (input.ready() && (line = input.readLine()) != null) {
    System.out.println(line);
}

if (p.waitFor() != 0) {
    Assert.fail("The process did not run succesfully.");
}

input.close();
}

非常感谢。

【问题讨论】:

标签: java


【解决方案1】:

我相信提示将转到 STDERR,而不是 STDOUT,因此您必须打开一个与之连接的流并在那里阅读。当您尝试从 STDOUT 读取时,您的代码会挂起,等待永远不会到达的输出。

编辑:我看到您在 ProcessBuilder 中重定向了错误流。

另一种可能是 BufferedReader 正在等待换行符完成读取,提示符不以换行符结尾。

【讨论】:

  • 没错,错误输出是重定向的,所以不是这样。另一种可能性可能是正确的,您知道如何解决这个问题,或者至少如何尝试确定是否发生了这种情况?
  • 读取无缓冲并将您读取的内容转储到本地标准输出以查看发生了什么。然后更改代码,使其在看到提示后立即发送密码,而无需等待换行符。
  • 我已经尝试了很多方法,但似乎都没有工作。我也尝试使用 Robot 类发送击键,但这也不起作用。当我调试测试时,它甚至在从输入流中读取第一个字符之前就卡住了,我不知道为什么会这样。
  • 好吧,那么唯一的可能是程序没有产生输出但也没有终止。正如您所描述的那样,这将使流保持打开和阻塞。如果您使用的是 Windows,您应该能够使用 Sysinternals.com 中的一种工具(我相信是 ProcessMonitor)来跟踪所有 I/O 并查看发生了什么。
  • 你有没有设法解决这个问题?这个question 看起来很相似,但那里也没有答案。
【解决方案2】:

已经很久了,但遇到了完全相同的问题。这是一个应该可以工作的 SSCCE:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Toto
{

    private static Logger logger = LoggerFactory.getLogger(Toto.class);

    static class Params 
    {
        public String getUserName() {
            return "PUT USERNAME HERE";
        }
        public String getHost() {
            return "PUT HOST HERE";
        }
        public String getDbName() {
            return "PUT DBNAME HERE";
        }
        public char[] getPassword() {
            return new char[]{'p','a','s','s','w','o','r','d'};
        }
        public String getSqlFile() {
            return "PUT SQL COMMAND FILE HERE";
        }
    }

    public static void main(String[] args)
    {
        Params params = new Params();

        try {
            final String userName   = params.getUserName();
            final String host       = params.getHost();
            final String dbName     = params.getDbName();
            final char[] pass       = params.getPassword();
            if (userName == null || host == null || dbName == null || pass == null) {
                logger.error("Missing the following info to execute the SQL command file: {} {} {} {}"  , userName  == null ? "username": ""
                    , host      == null ? "host"    : ""
                        , dbName    == null ? "database": ""
                            , pass      == null ? "password": "");
                return;
            }
            List<String> sb = new ArrayList<String>();
            sb.add("psql");
            sb.add("-h");
            sb.add(host);
            sb.add("-U");
            sb.add(userName);
            sb.add("-d");
            sb.add(dbName);
            sb.add("-f");
            sb.add(params.getSqlFile());
            //              sb.add("-W"); // force password prompt
            logger.debug("Executing the following command: {}", sb.toString());
            ProcessBuilder pb = new ProcessBuilder(sb);
            final Process p = pb.start();
            final BufferedReader stdinReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
            final BufferedReader stderrReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {

                        OutputStreamWriter s = new OutputStreamWriter(p.getOutputStream());
                        s.write(pass);
                        s.write(System.getProperty("line.separator"));
                        s.flush();
                        System.out.println("Pass written");
                    }
                    catch(IOException e)
                    {
                        logger.error("Exception raised in the thread writting password to psql", e);
                    }
                }
            }).start();

            new Thread(new Runnable()
            {

                @Override
                public void run()
                {
                    try
                    {
                        String s;
                        while (( s=stdinReader.readLine()) != null) {
                            logger.debug("psql [STDOUT]: {}", s);
                        }
                    }
                    catch(IOException e)
                    {
                        logger.error("Exception raised in thread displaying stdout of psql", e);
                    }
                }
            }).start();
            new Thread(new Runnable()
            {

                @Override
                public void run()
                {
                    try
                    {
                        String s;
                        while (( s=stderrReader.readLine()) != null) {
                            logger.error("psql [STDERR]: {}", s);
                        }
                    }
                    catch(IOException e)
                    {
                        logger.error("Exception raised in thread displaying stderr of psql", e);
                    }
                }
            }).start();
            int returnVal = p.waitFor();
            logger.debug("Process ended with return val {} ", returnVal);

        }
        catch (Exception e) {
            logger.error("Exception raised while executing the results on server", e);
        }

    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2017-08-15
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多