ProcessBuilder ve Runtime.exec () arasındaki fark


97

Java kodundan harici bir komut yürütmeye çalışıyorum, ancak Runtime.getRuntime().exec(...)ve arasında fark ettiğim bir fark var new ProcessBuilder(...).start().

Kullanırken Runtime:

Process p = Runtime.getRuntime().exec(installation_path + 
                                       uninstall_path + 
                                       uninstall_command + 
                                       uninstall_arguments);
p.waitFor();

exitValue 0 ve komut tamamlandı.

Ancak şu şekilde ProcessBuilder:

Process p = (new ProcessBuilder(installation_path +    
                                 uninstall_path +
                                 uninstall_command,
                                 uninstall_arguments)).start();
p.waitFor();

çıkış değeri 1001'dir ve komut waitFordönmesine rağmen ortada sona erer .

Sorunu çözmek için ne yapmalıyım ProcessBuilder?

Yanıtlar:


100

Çeşitli aşırı yüklemeler Runtime.getRuntime().exec(...)bir dizi dizgesini veya tek bir dizeyi alır. Tek dizeli aşırı yüklemeleri exec(), dize dizisini exec()bir dize dizisi alan aşırı yüklemelerden birine geçirmeden önce dizeyi bir argüman dizisine belirtecektir . ProcessBuilderKurucular, diğer taraftan, sadece dizeleri veya dizisi varargs almak Listdizi veya listedeki her dize bir birey argüman olarak kabul edilir dizeleri. Her iki durumda da, elde edilen argümanlar daha sonra çalıştırılmak üzere işletim sistemine iletilen bir dizede birleştirilir.

Yani, örneğin Windows'ta,

Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");

DoStuff.exeverilen iki argümanla bir program çalıştıracaktır . Bu durumda, komut satırı belirtilir ve yeniden bir araya getirilir. Ancak,

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");

Adını olan bir program olması umulur sürece başarısız olacaktır DoStuff.exe -arg1 -arg2içinde C:\. Bunun nedeni belirteçleştirme olmamasıdır: Çalıştırılacak komutun zaten belirtilmiş olduğu varsayılır. Bunun yerine kullanmalısınız

ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe", "-arg1", "-arg2");

Veya alternatif olarak

List<String> params = java.util.Arrays.asList("C:\DoStuff.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);

hala çalışmıyor: List <String> params = java.util.Arrays.asList (yükleme_yolu + uninstall_path + uninstall_command, uninstall_arguments); İşlem qq = yeni ProcessBuilder (parametreler) .start ();
gal

7
Bu dizge birleştirmenin herhangi bir anlam ifade ettiğine inanamıyorum: "yükleme_yolu + kaldırma_ yolu + kaldırma_ komutu".
Angel O'Sphere

8
Runtime.getRuntime (). Exec (...) yok DEĞİL bu sürece bir kabuk açıkça komutu ile belirlendiğinde çağırmak. Bu, son "Shellshock" hata sorunuyla ilgili iyi bir şeydir. Bu cevap yanıltıcıdır, çünkü cmd.exe veya eşdeğerinin (yani unix üzerinde / bin / bash) çalıştırılacağını belirtir, ki durum böyle görünmemektedir. Bunun yerine belirteçleştirme, Java ortamında yapılır.
Stefan Paul Noack

@ noah1989: geri bildirim için teşekkürler. Cevabımı (umarım) bazı şeyleri açıklığa kavuşturmak ve özellikle herhangi bir mermi veya cmd.exe.
Luke Woodward

exec için ayrıştırıcı da parametreleştirilmiş sürümle tamamen aynı şekilde çalışmıyor, bu da
Drew Delano

19

Bak nasıl Runtime.getRuntime().exec()String komutunu geçer ProcessBuilder. Bir belirteç kullanır ve komutu tek tek belirteçler halinde patlatır, ardından exec(String[] cmdarray, ......)hangi bir ProcessBuilder.

Tek ProcessBuilderbir dizi yerine bir dizeler dizisi ile inşa ederseniz , aynı sonuca ulaşırsınız.

ProcessBuilderYapıcı bir alan String...tek String olarak bütün komutunu geçen bir terminal içinde tırnak içinde bu komutu çağırarak aynı etkiye sahiptir, böylece vararg:

shell$ "command with args"

16

Arasında hiçbir fark yoktur ProcessBuilder.start()ve Runtime.exec()çünkü uygulaması Runtime.exec():

public Process exec(String command) throws IOException {
    return exec(command, null, null);
}

public Process exec(String command, String[] envp, File dir)
    throws IOException {
    if (command.length() == 0)
        throw new IllegalArgumentException("Empty command");

    StringTokenizer st = new StringTokenizer(command);
    String[] cmdarray = new String[st.countTokens()];
    for (int i = 0; st.hasMoreTokens(); i++)
        cmdarray[i] = st.nextToken();
    return exec(cmdarray, envp, dir);
}

public Process exec(String[] cmdarray, String[] envp, File dir)
    throws IOException {
    return new ProcessBuilder(cmdarray)
        .environment(envp)
        .directory(dir)
        .start();
}

Yani kod:

List<String> list = new ArrayList<>();
new StringTokenizer(command)
.asIterator()
.forEachRemaining(str -> list.add((String) str));
new ProcessBuilder(String[])list.toArray())
            .environment(envp)
            .directory(dir)
            .start();

şununla aynı olmalıdır:

Runtime.exec(command)

Yorumunuz için teşekkürler dave_thompson_085


2
Ama Q bu yöntemi çağırmıyor. (Dolaylı olarak) çağırır public Process exec(String command, String[] envp, File dir)- StringNOT String[]- StringTokenizerbelirteçleri çağıran ve daha sonra iletilen (dolaylı olarak) bir diziye yerleştirir ProcessBuilder, bu da 7 yıl önceki üç yanıtta doğru bir şekilde belirtildiği gibi bir farktır.
dave_thompson_085

Sorunun kaç yaşında olduğu önemli değil. Ama cevabı düzeltmeye çalışıyorum.
Eugene Lopatkin

ProcessBuilder için ortamı ayarlayamıyorum. Sadece çevreyi alabiliyorum ...
ilke Muhtaroğlu

Çevre yöntemiyle aldıktan sonra ortamı ayarlamak için docs.oracle.com/javase/7/docs/api/java/lang/… adresine bakın ...
ilke Muhtaroglu

Daha dikkatli bakarsanız, bu ortamın varsayılan olarak boş olduğunu görebilirsiniz.
Eugene Lopatkin

15

Evet bir fark var.

  • Runtime.exec(String)Yöntem, bir komuta ve bağımsız değişkenlerin bir dizi halinde böler bu tek bir komut dizisi alır.

  • ProcessBuilderYapıcı dizeleri (varargs) dizi alır. İlk dize komut adıdır ve geri kalanı argümanlardır. (Dizelerin bir listesini alan, ancak komut ve bağımsız değişkenlerden oluşan tek bir dize almayan alternatif bir kurucu vardır.)

Dolayısıyla, ProcessBuilder'a yapmasını söylediğiniz şey, adında boşluklar ve başka önemsiz şeyler olan bir "komutu" yürütmektir. Tabii ki, işletim sistemi bu isimde bir komut bulamaz ve komutun yürütülmesi başarısız olur.


Hayır, bir fark yok. Runtime.exec (String), ProcessBuilder için bir kısayoldur. Desteklenen başka kurucular da var.
marcolopes

2
Yanılıyorsun Kaynak kodunu okuyun! Runtime.exec(cmd)etkili bir kısayol Runtime.exec(cmd.split("\\s+")). ProcessBuilderSınıf doğrudan eşdeğer olan bir kurucu yoktur Runtime.exec(cmd). Cevabımda değindiğim nokta bu.
Stephen C

1
Eğer böyle bir ProcessBuilder örneğini Aslında,: new ProcessBuilder("command arg1 arg2"), start()çağrı beklediğiniz yapmayacağım. Muhtemelen başarısız olacak ve yalnızca adında boşluklar olan bir komutunuz varsa başarılı olacaktır. Bu tam da OP'nin sorduğu sorundur!
Stephen C
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.