我一直在搞亂 java 行程 API 并遇到以下情況:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
var command = "echo foo";
System.out.println(command);
run(command);
command = "bash -c 'echo bar'";
System.out.println(command);
run(command);
}
public static void run(String command) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
var br = new BufferedReader(new InputStreamReader(p.getInputStream()));
br.lines().forEach(System.out::println);
}
}
為什么不bar列印?我也嘗試過其他命令,如systemctl poweroffand mkdir,但似乎都沒有執行。我也嘗試過nohup,它可以單獨作業,但不能與bash -c.
uj5u.com熱心網友回復:
您呼叫exec(String),后者又呼叫exec(String,String,File),后者又使用StringTokenizer將作為單個字串傳遞的命令列切割為引數串列,然后呼叫exec(String[],字串,檔案)。
但是,該標記器只是在空格處進行切割(它不知道它正在使用命令列或將涉及什么 shell)。這意味著您最終將這些標記作為命令:bash, -c, 'echo, foo'--- 注意單引號;Runtime.exec不涉及 shell 來處理引號(或變數替換等)。
bash 然后抱怨'echo,但你沒有看到,因為你只列印子行程' stdout,而不是stderr. 將這樣的代碼添加到run:
br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
System.out.println("stderr:");
br.lines().forEach(System.out::println);
這讓我:
stderr:
bar': -c: line 1: unexpected EOF while looking for matching `''
bar': -c: line 2: syntax error: unexpected end of file
現在,如果您從呼叫中洗掉單引號,您只會得到一個空行,因為bash -c只需要運行一個引數,這里是echo,它會列印一個新行。
要解決此問題,您需要直接呼叫exec采用 a的版本,String[]以便您控制一個引數是什么:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
run("echo", "foo");
run("bash", "-c", "echo bar");
}
public static void run(String... command) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
var br = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("stdout:");
br.lines().forEach(System.out::println);
br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
System.out.println("stderr:");
br.lines().forEach(System.out::println);
}
}
uj5u.com熱心網友回復:
就個人而言,我會使用更高級別的東西,比如標準ProcessBuilder類或來自 Apache Commons的exec 庫,因為它們更好地支持構建復雜的命令來執行:
import java.io.IOException;
import org.apache.commons.exec.*;
public class ExecDemo {
// Build and execute a command with Apache Commons Exec
private static void demoApacheExec()
throws IOException, ExecuteException {
var cmd = new CommandLine("sh");
cmd.addArgument("-c");
cmd.addArgument("echo test 1", false);
System.out.println("Command: " cmd);
var executor = new DefaultExecutor();
executor.execute(cmd);
}
// Build and execute a command with ProcessBuilder
private static void demoProcessBuilder()
throws IOException, InterruptedException {
// Can also take a List<String> of arguments
var pb = new ProcessBuilder("sh", "-c", "echo test 2").inheritIO();
System.out.println("Command: " pb.command());
pb.start().waitFor();
}
public static void main(String[] args) {
try {
demoApacheExec();
demoProcessBuilder();
} catch (Exception e) {
System.err.println("Error executing program: " e);
System.exit(1);
}
}
}
重要的是讓每個引數成為自己獨立的東西,而不是像 shell 那樣依賴嘗試決議單個字串(ACE 有一個CommandLine.parse()函式,但即使它也有參考問題;addArgument喜歡將實際引號放在帶有空格的引數周圍當它實際上不需要時,除非你告訴它不要。)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/384437.html
上一篇:由tee發送到檔案的轉換流
下一篇:模擬打開檔案進行讀取時出錯
