2013-01-09 55 views
0

我正在編寫一個Java程序,需要從系統的/ dev/urandom接口中提取32個字符的鍵。對於這一點,我使用下列程序:Bash在Java中不能正確執行

public String generateKey() { 
      try { 
        Runtime run = Runtime.getRuntime(); 
        Process pr = run.exec("tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1;echo;"); 
        pr.waitFor(); 
        BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream())); 
        StringBuffer sb = new StringBuffer(); 
        String line = ""; 
        while ((line = buf.readLine())!= null) { 
          sb.append(line); 
        } 
        return sb.toString(); 
      } catch (Exception e) { 
        e.printStackTrace(); 
        return "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 
      } 
    } 

(如果不能做到這一點,然後它會返回一堆空字符)

出於某種原因,它返回什麼(或拋出異常並且不打印堆棧跟蹤)。我知道這一點,因爲我將它打印到控制檯(沒有),當我將它發送給我的RC4引擎時,它會拋出一個除零錯誤。爲什麼命令不起作用,我怎麼才能使它工作?

謝謝你的幫助。

編輯:我發佈這不是兩分鐘後,我推斷它沒有返回空字符,因爲我將它設置爲返回單詞「檸檬」,它做了同樣的事情,所以它沒有拋出異常。

編輯2:按在評論中我試圖從具有相同結果的文件向右讀,但我想我可能會做這種錯誤的建議......

File file = new File("/dev/urandom"); 
      FileReader freader = null; 
      StringBuffer sb = new StringBuffer(); 
      int x = 0; 
      try { 
        freader = new FileReader(file); 
        BufferedReader reader = new BufferedReader(freader); 
        for (int i=0;(i<32||(x=reader.read())==-1);i++) { 
          sb.append((char) x); 
        } 
      return sb.toString(); 
      } catch(Exception e) { 
        e.printStackTrace(); 
        return "a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 
      } 

編輯3 :這可能有助於那些幫助: 使用上面的代碼和管道輸出到od我發現它只是讀0。 (同樣,它不是拋出一個異常,否則它不會是全零:)

# ./run.sh | od 
0000000 000000 000000 000000 000000 000000 000000 000000 000000 
* 
+1

只要*不*在最小的測試案例中捕獲異常[尤其是] ..不需要處理檸檬NULs。 – 2013-01-09 00:11:31

+0

「不要發現異常」是什麼意思?如果我刪除了try/catch塊,編譯器會對我大吼。我認爲你的意思是不同的...... –

+0

閱讀*檢查例外* - 這可能是這樣惱人的小動物 - 和'拋出'條款。如果這對其他方法(例如更大的上下文)是不可取的,只需簡單地將它包裝爲Unchecked:try {..} catch(Exception ex){throw new RuntimeException(ex); }'。這個和普通的日誌記錄之間的區別是這仍然會傳播一個異常 - 耶!只有在存在某種有用的情況下才能抑制異常(這可能是「壓制它」的情況,而不是在這裏/在測試用例中)。 – 2013-01-09 00:14:44

回答

1

正如已經被用戶Miserable Variable,嘗試運行實際的Unix shell裏你的命令指示;並確保將shell作爲交互式shell進行調用(sh -i -c 'cmd1 | cmd2')。

否則,UNIX管道機制似乎被阻止,因爲tr繼續從/dev/urandom讀取,但由於某種原因拒絕寫入其stdout

Terminal.app雖然下面的命令運行時執行沒有任何打嗝:

sh -c 'LC_ALL=C tr -cd [:alnum:] < /dev/urandom | fold -w30 | head -n1; echo;'

另一種方法是顯式地限制從/dev/urandom讀取的字節的數量(見下面的代碼)。

/* 

# cat GenerateKey.java 
javac GenerateKey.java 
java GenerateKey 

# For more information see the "Java exec and Unix pipes" comment in: 
# "Java exec - execute system processes with Java ProcessBuilder and Process (part 3)", 
# http://alvinalexander.com/java/java-exec-processbuilder-process-3 

*/ 

import java.io.*; 
import java.util.*; 

public class GenerateKey { 
    public static void main(String[] args) throws Exception { 
     Runtime run = Runtime.getRuntime(); 

     // does not work on Mac OS X 10.6.8 
     //String[] cmd = { "/bin/sh", "-c", "LC_ALL=C tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1; echo;" }; 

     // works 
     String[] cmd = { "/bin/sh", "-i", "-c", "LC_ALL=C tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1; echo;" }; 
     //String[] cmd = { "/bin/sh", "-c", "head -c 3000 < /dev/urandom | LC_ALL=C tr -cd '[:alnum:]' | head -c 30" }; 
     //String[] cmd = { "/bin/sh", "-c", "dd if=/dev/urandom bs=3000 count=1 2>/dev/null | LC_ALL=C tr -cd '[:alnum:]' | fold -w30 | head -n1" }; 
     Process pr = run.exec(cmd); 

     pr.waitFor(); 
     BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream())); 
     String line; 
     while ((line = buf.readLine())!= null) { 
      System.out.println(line); 
     } 
    } 
} 
2

雖然這不是絕對必要的,以運行多個命令行參數的程序,你需要創建一個數組組成的命令字符串作爲第一個參數,並且每個空間應陣列的條目,例如用於LS -l

String[] command = {"ls" , "-l"}; 
Runtime.getRuntime().exec(command); 

這使得在命令行正確的呼叫。這裏是javadoc:Exec javadoc

+0

或簡單的'Runtime.getRuntime()。exec(「ls」,「-l」)' –

+1

不是必然的。 Runtime.exec(「/ bin/echo XXX」)完美工作。 – Archer

1

你在做Runtime.exec()時不能使用linux管道來運行幾個comman。您應該單獨運行命令或手動處理tr命令的結果。除此之外,您必須使用完整路徑命令二進制文件,例如將tr替換爲/usr/bin/tr

+0

不,命令將在'$ PATH'中找到 –

+0

否在該示例中。如果我嘗試使用'tr'語法,它會拋出'由...引發:java.io.IOException:java.io.IOException:error = 2,沒有這樣的文件或目錄。如果我用'/ usr/bin/tr'替換'tr',它會很好用 – Archer

4

Process pr = run.exec("tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1;echo;");

管(|)和重定向(<)由外殼,而不是OS處理。您可以通過將整個命令作爲字符串參數傳遞給sh來解決此問題。請看下面的例子,並調整它爲您的使用:

import java.io.*; 
class P1 { 
    public static void main(String[] args) throws Exception { 
     Runtime run = Runtime.getRuntime(); 
     Process pr = run.exec("sh -c \"ls </dev/null | grep java\""); 
     pr.waitFor(); 
     BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream())); 
     String line; 
     while ((line = buf.readLine())!= null) { 
      System.out.println(line); 
     } 
    } 
} 

編輯

雖然上面可能會幫助您運行使用重定向shell命令,在目前的情況下正確的做法是不使用外部過程在所有的,而不是從/dev/urandom直接讀取,如@Josh卡特賴特在this comment解釋