当我想跑的时候 Runtime.exec(String)
,某些命令工作,而其他命令执行,但失败或做不同的事情比我的终端。下面是一个独立的测试用例,它演示了以下效果:
public class ExecTest {
static void exec(String cmd) throws Exception {
Process p = Runtime.getRuntime().exec(cmd);
int i;
while( (i=p.getInputStream().read()) != -1) {
System.out.write(i);
}
while( (i=p.getErrorStream().read()) != -1) {
System.err.write(i);
}
}
public static void main(String[] args) throws Exception {
System.out.print("Runtime.exec: ");
String cmd = new java.util.Scanner(System.in).nextLine();
exec(cmd);
}
}
如果将命令替换为 echo hello world
,但对于其他命令,尤其是那些包含空格的文件名的命令,即使该命令正在执行,我也会遇到错误:
myshell$ javac ExecTest.java && java ExecTest
Runtime.exec: ls -l 'My File.txt'
ls: cannot access 'My: No such file or directory
ls: cannot access File.txt': No such file or directory
同时,复制粘贴到我的shell:
myshell$ ls -l 'My File.txt'
-rw-r--r-- 1 me me 4 Aug 2 11:44 My File.txt
为什么会有区别?它什么时候工作,什么时候失败?如何使其适用于所有命令?
1条答案
按热度按时间zengzsys1#
为什么有些命令会失败?
这是因为命令传递给
Runtime.exec(String)
不是在shell中执行的。shell为程序执行许多公共支持服务,当shell不在时,命令将失败。什么时候命令失败?
只要命令依赖于shell特性,它就会失败。shell做了很多我们通常不会想到的常见的、有用的事情:
shell在引号和空格上正确拆分
这样可以确保文件名
"My File.txt"
仍然是一个单一的论点。Runtime.exec(String)
简单地在空格上拆分,并将其作为两个单独的文件名传递。这显然是失败的。shell扩展globs/通配符
当你跑的时候
ls *.doc
,shell将其重写为ls letter.doc notes.doc
.Runtime.exec(String)
没有,它只是把它们作为参数传递。ls
不知道是什么*
是,所以命令失败。shell管理管道和重定向。
当你跑的时候
ls mydir > output.txt
,shell为命令输出打开“output.txt”,并将其从命令行中删除,给出ls mydir
.Runtime.exec(String)
不。它只是把它们当作论点来传递。ls
不知道是什么>
意思是,所以命令失败了。shell展开变量和命令
当你跑的时候
ls "$HOME"
或者ls "$(pwd)"
,shell将其重写为ls /home/myuser
.Runtime.exec(String)
没有,它只是把它们作为参数传递。ls
不知道是什么$
意思是,所以命令失败了。你能做些什么?
有两种方法可以执行任意复杂的命令:
简单而草率:委托给shell。
你可以用
Runtime.exec(String[])
(注意数组参数)并将命令直接传递给一个shell,该shell可以完成所有的繁重工作:安全可靠:承担壳牌公司的责任。
这不是一个可以机械地应用的修复方法,而是需要了解unix执行模型、shell的作用以及如何做到这一点。但是,您可以通过从图中去掉外壳来获得可靠、安全和健壮的解决方案。这是由
ProcessBuilder
.上一个示例中需要有人处理1的命令。引用,2。变量,和3。重定向,可以写为: