ProcessBuilder और Runtime.exec () के बीच अंतर


96

मैं जावा कोड से एक बाहरी कमांड निष्पादित करने की कोशिश कर रहा हूं, लेकिन एक अंतर है जिसे मैंने Runtime.getRuntime().exec(...)और के बीच देखा है new ProcessBuilder(...).start()

उपयोग करते समय Runtime:

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

बाहर निकलेंवैल्यू 0 है और कमांड ठीक है।

हालाँकि, इसके साथ ProcessBuilder:

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

निकास मान 1001 है और कमांड बीच में समाप्त हो जाती है, हालांकि waitForरिटर्न।

मुझे समस्या को ठीक करने के लिए क्या करना चाहिए ProcessBuilder?

जवाबों:


99

विभिन्न अधिभार Runtime.getRuntime().exec(...)या तो तार की एक सरणी या एक स्ट्रिंग लेते हैं। स्ट्रिंग स्ट्रिंग लेने वाले ओवरलोड exec()में से एक पर स्ट्रिंग एरे को पास करने से पहले, स्ट्रिंग के तर्कों को स्ट्रिंग के एकल-स्ट्रिंग ओवरलोड में बदल दिया जाएगा exec()ProcessBuilderकंस्ट्रक्टर्स, दूसरे हाथ पर, केवल एक तार या एक की सरणी varargs ले Listतार की है, जहां सरणी या सूची में प्रत्येक स्ट्रिंग एक व्यक्ति तर्क माना जाता है। किसी भी तरह से, प्राप्त तर्कों को एक स्ट्रिंग में शामिल किया जाता है जिसे निष्पादित करने के लिए ओएस को पास किया जाता है।

इसलिए, उदाहरण के लिए, विंडोज पर,

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

DoStuff.exeदो दिए गए तर्कों के साथ एक कार्यक्रम चलाएगा । इस स्थिति में, कमांड-लाइन टोकन हो जाती है और वापस एक साथ रख दी जाती है। तथापि,

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

, असफल हो जायेगी जब तक कि वहाँ एक कार्यक्रम जिसका नाम है होता है DoStuff.exe -arg1 -arg2में C:\। इसका कारण यह है कि कोई टोकन नहीं है: चलाने के लिए आदेश को पहले से ही टोकन मान लिया गया है। इसके बजाय, आपको उपयोग करना चाहिए

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

या वैकल्पिक रूप से

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

यह अभी भी काम नहीं करता है: सूची <string> params = java.util.Arrays.asList (installation_path + uninstall_path + uninstall_command, uninstall_arguments); प्रक्रिया qq = new ProcessBuilder (params) .start ();
गैल

7
मुझे विश्वास नहीं हो रहा है कि इस स्ट्रिंग कॉन्फैक्शन का कोई मतलब है: "इंस्टालेशन_पथ + अनइंस्टॉल_पैथ + अनइंस्टाल_कमांड"।
एंजेल ओ 'स्फ़ेयर

8
Runtime.getRuntime ()। Exec (...) एक शेल को तब तक लागू नहीं करता है जब तक कि वह स्पष्ट रूप से कमांड द्वारा निर्दिष्ट नहीं किया गया हो। हाल ही में "शेलशॉक" बग समस्या के बारे में यह एक अच्छी बात है। यह उत्तर भ्रामक है, क्योंकि यह बताता है कि cmd.exe या समतुल्य (यानी यूनिक्स पर बिन / बैश) चलाया जाएगा, जो मामला नहीं लगता है। इसके बजाय जावा वातावरण के अंदर टोकन किया जाता है।
स्टीफन पॉल नैक

@ noah1989: प्रतिक्रिया के लिए धन्यवाद। मैंने अपना जवाब अपडेट किया है (उम्मीद है) चीजों को स्पष्ट करूंगा और विशेष रूप से गोले के किसी भी उल्लेख को हटा दूंगा या cmd.exe
ल्यूक वुडवर्ड

निष्पादन के लिए पार्सर या तो पैरामीटर किए गए संस्करण के समान काम नहीं करता है, जो मुझे पता लगाने में कुछ दिन लगते हैं ...
ड्रू डेलानो

18

Runtime.getRuntime().exec()स्ट्रिंग कमांड को कैसे देखें ProcessBuilder। यह एक टोकन का उपयोग करता है और व्यक्तिगत टोकन में कमांड को विस्फोट करता है, फिर इनवोक करता है exec(String[] cmdarray, ......)जो निर्माण करता है ProcessBuilder

यदि आप ProcessBuilderएक एकल के बजाय स्ट्रिंग्स की एक सरणी के साथ निर्माण करते हैं , तो आप उसी परिणाम पर पहुंचेंगे।

ProcessBuilderनिर्माता एक लेता है String..., vararg तो एक भी स्ट्रिंग के रूप में पूरे आदेश पारित करने के लिए एक टर्मिनल में उद्धरण में है कि आदेश लागू जैसा ही होता है:

shell$ "command with args"

14

के कार्यान्वयन में कोई अंतर नहीं है ProcessBuilder.start()और Runtime.exec()क्योंकि 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();
}

तो कोड:

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();

समान होना चाहिए:

Runtime.exec(command)

धन्यवाद dave_thompson_085 टिप्पणी के लिए


2
लेकिन क्यू उस विधि को कॉल नहीं करता है। यह (अप्रत्यक्ष रूप से) कॉल करता है public Process exec(String command, String[] envp, File dir)- Stringनहीं String[]- जो कॉल करता है StringTokenizerऔर टोकन को एक सरणी में डालता है, जिसे तब (अप्रत्यक्ष रूप से) पास किया जाता है ProcessBuilder, जो कि 7 साल पहले के तीन उत्तरों द्वारा बताए गए अनुसार अंतर है।
dave_thompson_085

इससे कोई फर्क नहीं पड़ता कि प्रश्न कितना पुराना है। लेकिन मैं जवाब को ठीक करने की कोशिश करता हूं।
यूजीन लोपाटकिन

मैं ProcessBuilder के लिए वातावरण सेट नहीं कर सकता। मैं केवल पर्यावरण प्राप्त कर सकता हूं ...
ilke Muhtaroglu

देखें docs.oracle.com/javase/7/docs/api/java/lang/… उन्हें पर्यावरण विधि के माध्यम से प्राप्त करने के बाद पर्यावरण को स्थापित करने के लिए ...
ilke Muhtaroglu

यदि आप अधिक ध्यान से देखते हैं तो आप उस वातावरण को डिफ़ॉल्ट रूप से देख सकते हैं।
यूजीन लोपाटकिन

14

हां, वहां एक अंतर है।

  • Runtime.exec(String)विधि एक एकल कमांड स्ट्रिंग है कि यह एक कमान और तर्क का एक दृश्य में विभाजित हो लेता है।

  • ProcessBuilderनिर्माता तार के एक (varargs) सरणी लेता है। पहला स्ट्रिंग कमांड नाम है और उनमें से बाकी तर्क हैं। (एक वैकल्पिक कंस्ट्रक्टर है जो स्ट्रिंग्स की एक सूची लेता है, लेकिन कोई भी ऐसा नहीं है जो कमांड और तर्कों से मिलकर एक स्ट्रिंग लेता है।)

तो आप क्या करने के लिए ProcessBuilder बता रहे हैं एक "कमांड" निष्पादित करना है जिसका नाम रिक्त स्थान है और इसमें अन्य कबाड़ है। बेशक, ऑपरेटिंग सिस्टम को उस नाम के साथ कमांड नहीं मिल सकता है, और कमांड निष्पादन विफल हो जाता है।


नहीं, कोई अंतर नहीं है। Runtime.exec (स्ट्रिंग) ProcessBuilder के लिए एक शॉर्टकट है। अन्य निर्माणकर्ता समर्थित हैं।
Marcolopes

2
आप गलत हैं। स्रोत कोड पढ़ें! Runtime.exec(cmd)प्रभावी ढंग से करने के लिए एक शॉर्टकट है Runtime.exec(cmd.split("\\s+"))ProcessBuilderकक्षा एक निर्माता के लिए एक सीधा बराबर है कि नहीं है Runtime.exec(cmd)। यह वह बिंदु है जो मैं अपने उत्तर में बना रहा हूं।
स्टीफन C

1
वास्तव में, यदि आप इस प्रकार का ProcessBuilder का दृष्टांत: new ProcessBuilder("command arg1 arg2"), start()कॉल आप क्या उम्मीद नहीं करेंगे। यह शायद विफल हो जाएगा, और केवल तभी सफल होगा जब आपके पास इसके नाम के रिक्त स्थान के साथ एक कमान होगी। यह ठीक वही समस्या है जिसके बारे में ओपी पूछ रहा है!
स्टीफन सी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.