为什么有些命令失败?
发生这种情况是因为命令传递给Runtime.exec(String)
不在 shell 中执行。 shell 为程序执行许多常见的支持服务,当 shell 无法执行这些服务时,命令将失败。
命令什么时候会失败?
只要命令依赖于 shell 功能,它就会失败。 shell 做了很多我们通常不会想到的常见、有用的事情:
-
shell 在引号和空格上正确分割
这确保了文件名"My File.txt"
仍然是一个单一的论点。
Runtime.exec(String)
天真地分割空格并将其作为两个单独的文件名传递。这显然失败了。
-
shell 扩展了全局/通配符
当你跑步时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"
or ls "$(pwd)"
,shell将其重写为ls /home/myuser
.
Runtime.exec(String)
不,它只是将它们作为参数传递。
ls
不知道什么$
意味着,所以命令失败。
你能做什么呢?
有两种方法可以执行任意复杂的命令:
简单又马虎:委托给 shell。
你可以只使用Runtime.exec(String[])
(注意数组参数)并将命令直接传递到可以完成所有繁重工作的 shell:
// Simple, sloppy fix. May have security and robustness implications
String myFile = "some filename.txt";
String myCommand = "cp -R '" + myFile + "' $HOME 2> errorlog";
Runtime.getRuntime().exec(new String[] { "bash", "-c", myCommand });
安全、稳健:承担shell的职责。
这不是一个可以机械应用的修复方法,而是需要了解 Unix 执行模型、shell 的作用以及如何执行相同的操作。然而,您可以通过去掉外壳来获得可靠、安全和强大的解决方案。这是通过以下方式促进的:ProcessBuilder
.
上一个示例中的命令需要某人处理 1. 引号、2. 变量和 3. 重定向,可以写为:
String myFile = "some filename.txt";
ProcessBuilder builder = new ProcessBuilder(
"cp", "-R", myFile, // We handle word splitting
System.getenv("HOME")); // We handle variables
builder.redirectError( // We set up redirections
ProcessBuilder.Redirect.to(new File("errorlog")));
builder.start();