खोज…


टिप्पणियों

ध्यान दें कि API अनुशंसा करता है कि, संस्करण 1.5 के रूप में, प्रक्रिया बनाने का पसंदीदा तरीका ProcessBuilder.start() का उपयोग कर रहा है।

एक अन्य महत्वपूर्ण टिप्पणी यह है कि waitFor द्वारा उत्पादित निकास मूल्य कार्यक्रम निष्पादित / स्क्रिप्ट से निर्भर है। उदाहरण के लिए, calc.exe द्वारा निर्मित निकास कोड notepad.exe से अलग हैं।

सरल उदाहरण (जावा संस्करण <1.5)

यह उदाहरण विंडोज़ कैलकुलेटर को कॉल करेगा। यह ध्यान रखना महत्वपूर्ण है कि बाहर निकलने का कोड उस प्रोग्राम / स्क्रिप्ट के अनुसार अलग-अलग होगा जिसे कॉल किया जा रहा है।

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

ProcessBuilder वर्ग का उपयोग करना

ProcessBuilder वर्ग कमांड लाइन के माध्यम से कमांड भेजना आसान बनाता है। इसके लिए आवश्यक स्ट्रिंग्स की एक सूची है जो दर्ज किए जाने वाले आदेशों को बनाती है। आप कमांड को निष्पादित करने के लिए बस अपने प्रोसेसब्यूरी उदाहरण पर स्टार्ट () विधि कहते हैं।

यदि आपके पास Add.exe नामक एक कार्यक्रम है जो दो तर्क लेता है और उन्हें जोड़ता है, तो कोड कुछ इस तरह दिखाई देगा:

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

ध्यान रखने योग्य कुछ बातें:

  • आदेशों की सरणी सभी एक स्ट्रिंग सरणी होनी चाहिए
  • आदेशों को क्रम में (एरे में) होना चाहिए कि यदि आप कमांड में ही प्रोग्राम को कॉल करते हैं, तो (यानी।) का नाम पहले तर्क के बाद नहीं जा सकता है।
  • कार्य निर्देशिका को सेट करते समय आपको एक फ़ाइल ऑब्जेक्ट में पास करने की आवश्यकता होती है, न कि केवल एक स्ट्रिंग के रूप में फ़ाइल का नाम

ब्लॉकिंग बनाम नॉन-ब्लॉकिंग कॉल

सामान्य तौर पर कमांड लाइन पर कॉल करते समय, प्रोग्राम कमांड भेजेगा और फिर इसका निष्पादन जारी रखेगा।

हालाँकि आप अपने स्वयं के निष्पादन को जारी रखने से पहले समाप्त होने के लिए कॉल किए गए प्रोग्राम का इंतजार करना चाहते हैं।

यह आसानी से लौटे Process उदाहरण से waitFor() विधि को कॉल करके किया जा सकता है।

उपयोग उदाहरण:

//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

कच्चे java.lang.ProcessBuilder एपीआई का उपयोग करके सीधे जावा से बाहरी प्रक्रियाओं को लॉन्च करना थोड़ा बोझिल हो सकता है। Apache Commons Exec लाइब्रेरी इसे थोड़ा आसान बनाती है। Ch.vorburger.exec लाइब्रेरी आगे कॉमन एक्ज़ेक पर फैली हुई है जो इसे वास्तव में सुविधाजनक बनाती है:

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

नुकसान: Runtime.exec, Process और ProcessBuilder शेल सिंटैक्स को नहीं समझते हैं

Runtime.exec(String ...) और Runtime.exec(String) विधियाँ आपको एक बाहरी प्रक्रिया 1 के रूप में एक कमांड निष्पादित करने की अनुमति देती हैं। पहले संस्करण में, आप कमांड नाम और कमांड तर्कों को स्ट्रिंग सरणी के अलग-अलग तत्वों के रूप में आपूर्ति करते हैं, और जावा रनटाइम बाहरी कमांड शुरू करने के लिए ओएस रनटाइम सिस्टम का अनुरोध करता है। दूसरा संस्करण भ्रामक रूप से उपयोग करने में आसान है, लेकिन इसमें कुछ नुकसान हैं।

सबसे पहले, यहां exec(String) को सुरक्षित रूप से उपयोग किए जाने का एक उदाहरण है:

Process p = Runtime.exec("mkdir /tmp/testDir");
p.waitFor();
if (p.exitValue() == 0) {
    System.out.println("created the directory");
}

पथिकों में रिक्त स्थान

मान लीजिए कि हम ऊपर दिए गए उदाहरण को सामान्य करते हैं ताकि हम एक मनमाना निर्देशिका बना सकें:

Process p = Runtime.exec("mkdir " + dirPath);
// ...

यह आम तौर पर काम करेगा, लेकिन यह विफल हो जाएगा अगर dirPath है (उदाहरण के लिए) "/ घर / उपयोगकर्ता / मेरे दस्तावेज़"। समस्या यह है कि exec(String) स्ट्रिंग को एक कमांड में विभाजित करता है और बस व्हाट्सएप की तलाश में तर्क देता है। कमांड स्ट्रिंग:

"mkdir /home/user/My Documents"

में विभाजित किया जाएगा:

"mkdir", "/home/user/My", "Documents"

और यह "mkdir" कमांड को विफल करने का कारण होगा क्योंकि यह एक तर्क की उम्मीद करता है, दो नहीं।

इसका सामना करते हुए, कुछ प्रोग्रामर पथनाम के आसपास उद्धरण जोड़ने की कोशिश करते हैं। यह भी काम नहीं करता है:

"mkdir \"/home/user/My Documents\""

में विभाजित किया जाएगा:

"mkdir", "\"/home/user/My", "Documents\""

रिक्त स्थान को "उद्धरण" करने के प्रयास में जोड़े गए अतिरिक्त दोहरे-उद्धरण वर्णों को किसी भी अन्य गैर-व्हाट्सएप वर्ण की तरह व्यवहार किया जाता है। वास्तव में, हम जो कुछ भी करते हैं या रिक्त स्थान से बचते हैं वह विफल हो रहा है।

इस विशेष समस्याओं से निपटने का तरीका exec(String ...) अधिभार का उपयोग करना है।

Process p = Runtime.exec("mkdir", dirPath);
// ...

यह काम करेगा अगर dirpath में dirpath वर्ण शामिल हैं क्योंकि exec का यह अधिभार तर्कों को विभाजित करने का प्रयास नहीं करता है। स्ट्रिंग्स को OS exec प्रणाली कॉल के माध्यम से पारित किया जाता है।

पुनर्निर्देशन, पाइपलाइन और अन्य शेल सिंटैक्स

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

Process p = Runtime.exec("find / -name *.java -print 2>/dev/null");

या

Process p = Runtime.exec("find source -name *.java | xargs grep package");

(पहला उदाहरण फ़ाइल सिस्टम में सभी जावा फाइलों के नामों को सूचीबद्ध करता है, और दूसरा package "स्टेटस" ट्री में जावा फाइलों में package स्टेटमेंट 2 को प्रिंट करता है।)

ये उम्मीद के मुताबिक काम नहीं करने वाले हैं। पहले मामले में, "खोज" कमांड को कमांड तर्क के रूप में "2> / देव / नल" के साथ चलाया जाएगा। इसकी व्याख्या पुनर्निर्देशन के रूप में नहीं की जाएगी। दूसरे उदाहरण में, पाइप चरित्र ("|") और इसके बाद के कार्यों को "खोज" कमांड दिया जाएगा।

यहां समस्या यह है कि exec तरीके और ProcessBuilder किसी भी शेल सिंटैक्स को नहीं समझते हैं। इसमें पुनर्निर्देशन, पाइपलाइन, चर विस्तार, ग्लोबिंग, और इसी तरह शामिल हैं।

कुछ मामलों में (उदाहरण के लिए, सरल पुनर्निर्देशन) आप आसानी से ProcessBuilder का उपयोग करके वांछित प्रभाव प्राप्त कर सकते हैं। हालांकि, यह सामान्य रूप से सच नहीं है। एक शेल में कमांड लाइन को चलाने के लिए एक वैकल्पिक दृष्टिकोण है; उदाहरण के लिए:

Process p = Runtime.exec("bash", "-c", 
                         "find / -name *.java -print 2>/dev/null");

या

Process p = Runtime.exec("bash", "-c", 
                         "find source -name \\*.java | xargs grep package");

लेकिन ध्यान दें कि दूसरे उदाहरण में, हमें वाइल्डकार्ड चरित्र ("*") से बचने की आवश्यकता थी क्योंकि हम चाहते हैं कि वाइल्डकार्ड शेल के बजाय "खोज" द्वारा व्याख्या की जाए।

शेल बिलिन कमांड काम नहीं करते हैं

मान लें कि निम्नलिखित उदाहरण UNIX जैसे शेल वाले सिस्टम पर काम नहीं करेंगे:

Process p = Runtime.exec("cd", "/tmp");     // Change java app's home directory

या

Process p = Runtime.exec("export", "NAME=value");  // Export NAME to the java app's environment

यह काम नहीं करेगा, इसके कुछ कारण हैं:

  1. "सीडी" और "निर्यात" कमांड पर शेल बिलिन कमांड हैं। वे अलग-अलग निष्पादन योग्य के रूप में मौजूद नहीं हैं।

  2. शेल बिल्डिंस के लिए वे जो करने वाले हैं (जैसे कि वर्किंग डायरेक्टरी को बदलना, पर्यावरण को अपडेट करना), उन्हें उस जगह को बदलने की जरूरत है, जहां वह रहता है। एक सामान्य एप्लिकेशन (जावा एप्लिकेशन सहित) के लिए राज्य आवेदन प्रक्रिया से जुड़ा हुआ है। इसलिए, उदाहरण के लिए, "cd" कमांड चलाने वाली चाइल्ड प्रोसेस अपने पैरेंट "जावा" प्रोसेस की वर्किंग डायरेक्टरी को नहीं बदल सकती है। इसी तरह, एक exec की प्रक्रिया इस प्रक्रिया के लिए काम करने वाली निर्देशिका को बदल नहीं सकती है।

यह तर्क सभी शेल बिलिन कमांड पर लागू होता है।


1 - आप ProcessBuilder उपयोग कर सकते हैं, लेकिन यह इस उदाहरण के बिंदु के लिए प्रासंगिक नहीं है।

2 - यह थोड़ा मोटा और तैयार है ... लेकिन एक बार फिर, इस दृष्टिकोण की असफलता उदाहरण के लिए प्रासंगिक नहीं है।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow