2010-07-13 130 views
2

我想用Java使用ProcessBuilder執行一個外部程序,但它需要來自用戶的輸入。使用ProcessBuilder執行外部程序並提供輸入

更具體地說,該程序是psql(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(); 
} 

非常感謝。

+0

看到這裏http://stackoverflow.com/questions/2969766/process-requires-redirected-input – Mike 2012-03-05 03:06:40

回答

0

我相信提示是去STDERR,而不是STDOUT,所以你必須打開一個流連接到那裏,並閱讀。當您嘗試從STDOUT讀取時,代碼會掛起,等待永遠不會到達的輸出。

編輯:我看到你重定向了ProcessBuilder中的錯誤流。

另一種可能性是BufferedReader正在等待換行符讀完,並且提示不會以換行符結束。

+0

這是正確的,錯誤輸出重定向,所以它不是它。 其他可能性可能是對的,你知道如何解決這個問題,或者至少如何去確定這是怎麼回事? – 2010-07-13 20:31:43

+0

讀取未緩衝並將您讀取的內容轉儲到本地stdout以查看發生了什麼。然後更改代碼,以便在看到提示後立即發送密碼,而不必等待換行符。 – 2010-07-13 20:34:24

+0

這似乎也沒有工作。 – 2010-07-13 21:13:28

0

過了很久,卻遇到了完全相同的問題。下面是應該工作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); 
     } 

    } 

}