我正在编写一个需要从系统的/ dev / urandom接口中提取32个字符的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引擎时,它会抛出零除错误。为什么命令不起作用,我怎么能让它工作?
感谢您的帮助。
编辑:我发布此消息后不到两分钟我推断它没有返回空字符,因为我将其设置为返回单词“lemons”并且它做了同样的事情所以它不会抛出异常。
编辑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
*
答案 0 :(得分:4)
Process pr = run.exec("tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1;echo;");
管道(|
)和重定向(<
)由shell处理,而不是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 Cartwright在{{ 3}}
答案 1 :(得分:2)
虽然这不是绝对必要的,但是为了运行具有多个命令行参数的程序,您需要创建一个由命令组成的字符串数组作为第一个参数,并且每个空格应该是数组的一个条目, ls -l </ p>的示例
String[] command = {"ls" , "-l"};
Runtime.getRuntime().exec(command);
这样可以正确调用命令行。这是javadoc:Exec javadoc
答案 2 :(得分:1)
执行Runtime.exec()
时,不能使用linux管道来运行多个commans。您应该单独运行命令或手动处理tr
命令的结果。除此之外,您必须使用完整路径来命令二进制文件,例如将tr
替换为/usr/bin/tr
。
答案 3 :(得分: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);
}
}
}