Java Language
Bearbeta
Sök…
Anmärkningar
Observera att API rekommenderar att från och med version 1.5 är det föredragna sättet att skapa en process att använda ProcessBuilder.start()
.
En annan viktig kommentar är att utgångsvärdet som produceras av waitFor
är beroende av programmet / skriptet som körs. Exempelvis skiljer sig utgångskoderna som produceras av calc.exe från notepad.exe .
Enkelt exempel (Java-version <1,5)
Det här exemplet kommer att kalla Windows-kalkylatorn. Det är viktigt att lägga märke till att utgångskoden kommer att variera i enlighet med det program / skript som anropas.
package process.example;
import java.io.IOException;
public class App {
public static void main(String[] args) {
try {
// Executes windows calculator
Process p = Runtime.getRuntime().exec("calc.exe");
// Wait for process until it terminates
int exitCode = p.waitFor();
System.out.println(exitCode);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Använda klassen ProcessBuilder
ProcessBuilder-klassen gör det enkelt att skicka ett kommando genom kommandoraden. Allt det kräver är en lista över strängar som utgör de kommandon som ska matas in. Du kallar helt enkelt start () -metoden i din ProcessBuilder-instans för att köra kommandot.
Om du har ett program som heter Add.exe som tar två argument och lägger till dem, skulle koden se ut så här:
List<String> cmds = new ArrayList<>();
cmds.add("Add.exe"); //the name of the application to be run
cmds.add("1"); //the first argument
cmds.add("5"); //the second argument
ProcessBuilder pb = new ProcessBuilder(cmds);
//Set the working directory of the ProcessBuilder so it can find the .exe
//Alternatively you can just pass in the absolute file path of the .exe
File myWorkingDirectory = new File(yourFilePathNameGoesHere);
pb.workingDirectory(myWorkingDirectory);
try {
Process p = pb.start();
} catch (IOException e) {
e.printStackTrace();
}
Några saker att tänka på:
- Uppsättningen av kommandon måste alla vara en stränguppsättning
- Kommandona måste vara i den ordning (i matrisen) som de skulle vara om du ringde till programmet i själva kommandoraden (dvs. namnet på .exe kan inte gå efter det första argumentet
- När du ställer in arbetskatalogen måste du skicka ett filobjekt och inte bara filnamnet som en sträng
Blockering kontra icke-blockerande samtal
Generellt när programmet ringer till kommandoraden skickar programmet kommandot och fortsätter sedan dess körning.
Men du kanske vill vänta på att det kallade programmet avslutas innan du fortsätter med din egen exekvering (t.ex.
Detta kan enkelt göras genom att ringa waitFor()
från den returnerade Process
.
Exempel på användning:
//code setting up the commands omitted for brevity...
ProcessBuilder pb = new ProcessBuilder(cmds);
try {
Process p = pb.start();
p.waitFor();
} catch (IOException e) {
e.printStackTrack();
} catch (InterruptedException e) {
e.printStackTrace();
}
//more lines of code here...
ch.vorburger.exec
Att starta externa processer från Java med hjälp av raw java.lang.ProcessBuilder API direkt kan vara lite besvärligt. Apache Commons Exec-biblioteket gör det lite enklare. Ch.vorburger.exec-biblioteket sträcker sig vidare över Commons Exec för att göra det verkligen bekvämt:
ManagedProcess proc = new ManagedProcessBuilder("path-to-your-executable-binary")
.addArgument("arg1")
.addArgument("arg2")
.setWorkingDirectory(new File("/tmp"))
.setDestroyOnShutdown(true)
.setConsoleBufferMaxLines(7000)
.build();
proc.start();
int status = proc.waitForExit();
int status = proc.waitForExitMaxMsOrDestroy(3000);
String output = proc.getConsole();
proc.startAndWaitForConsoleMessageMaxMs("started!", 7000);
// use service offered by external process...
proc.destroy();
Fallgrop: Runtime.exec, Process och ProcessBuilder förstår inte skal-syntax
Runtime.exec(String ...)
och Runtime.exec(String)
låter dig köra ett kommando som en extern process 1 . I den första versionen tillhandahåller du kommandonamnet och kommandoargumenten som separata element i strängarrayen och Java-runtime begär OS-runtime-systemet att starta det externa kommandot. Den andra versionen är bedrägligt lätt att använda, men den har några fallgropar.
Först av allt, här är ett exempel på att använda exec(String)
som används på ett säkert sätt:
Process p = Runtime.exec("mkdir /tmp/testDir");
p.waitFor();
if (p.exitValue() == 0) {
System.out.println("created the directory");
}
Utrymmen i sökväg
Anta att vi generaliserar exemplet ovan så att vi kan skapa en godtycklig katalog:
Process p = Runtime.exec("mkdir " + dirPath);
// ...
Detta fungerar vanligtvis, men det misslyckas om dirPath
är (till exempel) "/ home / user / My Documents". Problemet är att exec(String)
delar strängen i ett kommando och argument genom att helt enkelt leta efter mellanrum. Kommandosträngen:
"mkdir /home/user/My Documents"
kommer att delas upp i:
"mkdir", "/home/user/My", "Documents"
och detta kommer att göra att "mkdir" -kommandot misslyckas eftersom det förväntar sig ett argument, inte två.
Inför detta försöker vissa programmerare lägga till citat runt sökvägen. Det fungerar inte heller:
"mkdir \"/home/user/My Documents\""
kommer att delas upp i:
"mkdir", "\"/home/user/My", "Documents\""
De extra karaktärerna med dubbla citat som lades till för att försöka "citera" utrymmena behandlas som alla andra tecken utan vitrum. Faktum är att allt vi citerar eller undviker utrymmena kommer att misslyckas.
Sättet att hantera just dessa problem är att använda exec(String ...)
överbelastning.
Process p = Runtime.exec("mkdir", dirPath);
// ...
Det här fungerar om dirpath
innehåller blankstegstecken eftersom denna överbelastning av exec
inte försöker dela argumenten. Strängarna överförs till OS exec
systemsamtal som de är.
Omdirigering, rörledningar och annan skalsyntax
Anta att vi vill omdirigera ett externt kommandos ingång eller utgång, eller köra en pipeline. Till exempel:
Process p = Runtime.exec("find / -name *.java -print 2>/dev/null");
eller
Process p = Runtime.exec("find source -name *.java | xargs grep package");
(Det första exemplet visar namnen på alla Java-filer i filsystemet, och det andra skriver ut package
2 i Java-filerna i källträdet.)
Dessa kommer inte att fungera som förväntat. I det första fallet kommer "find" -kommandot att köras med "2> / dev / null" som ett kommandoargument. Det tolkas inte som en omdirigering. I det andra exemplet kommer rörtecken ("|") och verken som följer det att ges till "find" -kommandot.
Problemet här är att exec
metoderna och ProcessBuilder
inte förstår någon skalsyntax. Detta inkluderar omdirigeringar, rörledningar, variabel expansion, globbing och så vidare.
I några få fall (till exempel enkel omdirigering) kan du enkelt uppnå önskad effekt med hjälp av ProcessBuilder
. Detta är emellertid inte sant i allmänhet. Ett alternativt tillvägagångssätt är att köra kommandoraden i ett skal; till exempel:
Process p = Runtime.exec("bash", "-c",
"find / -name *.java -print 2>/dev/null");
eller
Process p = Runtime.exec("bash", "-c",
"find source -name \\*.java | xargs grep package");
Men observera att i det andra exemplet behövde vi undkomma jokerteckenet ("*") eftersom vi vill att jokertecknet ska tolkas av "hitta" snarare än skalet.
Shell-inbyggda kommandon fungerar inte
Anta att följande exempel inte fungerar på ett system med ett UNIX-liknande skal:
Process p = Runtime.exec("cd", "/tmp"); // Change java app's home directory
eller
Process p = Runtime.exec("export", "NAME=value"); // Export NAME to the java app's environment
Det finns några orsaker till att detta inte fungerar:
På "cd" och "export" kommandon är shell-inbyggda kommandon. De finns inte som distinkta körbara filer.
För att inbyggda skal ska göra vad de ska göra (t.ex. ändra arbetskatalogen, uppdatera miljön) måste de ändra platsen där det tillståndet finns. För en normal applikation (inklusive en Java-applikation) är tillståndet associerat med applikationsprocessen. Så till exempel, barnprocessen som skulle köra kommandot "cd" kunde inte ändra arbetskatalogen för dess överordnade "java" -process. På samma sätt kan en
exec
process inte ändra arbetskatalogen för en process som följer den.
Detta resonemang gäller alla kommandon med inbyggda skal.
1 - Du kan också använda ProcessBuilder
, men det är inte relevant för exemplet.
2 - Detta är lite grovt och redo ... men än en gång är misslyckandet med denna metod inte relevant för exemplet.