简体   繁体   English

使用 ProcessBuilder 执行外部程序并提供输入

[英]Execute external program using ProcessBuilder and provide input

I'm trying to execute an external program with Java using ProcessBuilder , but it expects input from the user.我正在尝试使用ProcessBuilder使用 Java 执行外部程序,但它需要来自用户的输入。

To be more specific, the program is PGSQL (Postgres SQL), when it's executed the program prompts the user for a password.更具体地说,该程序是PGSQL (Postgres SQL),当它执行时,程序会提示用户输入密码。 The only way to bypass that is to save a file in the user home containing the passwords, I'm trying to avoid that, so I want to execute the program from Java and send the password using the process' output stream.绕过它的唯一方法是在用户主目录中保存一个包含密码的文件,我试图避免这种情况,所以我想从 Java 执行程序并使用进程的输出流发送密码。

The code works fine when the program doesn't expect any user input, but when I delete the password file from the user home, the program hangs.当程序不需要任何用户输入时,代码工作正常,但是当我从用户主页删除密码文件时,程序挂起。 I see that it's being executed, but nothing happens.我看到它正在执行,但没有任何反应。 If I debug it, it reaches the while and then nothing happens until I kill the process.如果我调试它,它会到达一段时间,然后在我终止进程之前什么也不会发生。

This is the code, any help will be greatly appreciated.这是代码,任何帮助将不胜感激。

@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();
}

Thanks a lot.非常感谢。

Been a long time, but got exactly the same problem.已经很长时间了,但遇到了完全相同的问题。 Here is a SSCCE that should work:这是一个应该有效的 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);
        }

    }

}

I believe the prompt is going to STDERR, not STDOUT, so you'll have to open a stream connected to that and read there.我相信提示将转到 STDERR,而不是 STDOUT,因此您必须打开连接到它的流并在那里阅读。 When you try to read from STDOUT your code hangs waiting for output that will never arrive.当您尝试从 STDOUT 读取时,您的代码会挂起,等待永远不会到达的输出。

EDIT: I see you have redirected the error stream in the ProcessBuilder.编辑:我看到您在 ProcessBuilder 中重定向了错误流。

One other possibility is that the BufferedReader is waiting for a newline to finish reading, and the prompt does not end with a newline.另一种可能性是 BufferedReader 正在等待换行符完成读取,并且提示没有以换行符结束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM