प्रोसेस.स्टार्ट: आउटपुट कैसे प्राप्त करें?


306

मैं अपने मोनो / .NET ऐप से एक बाहरी कमांड लाइन प्रोग्राम चलाना चाहूंगा। उदाहरण के लिए, मैं मेन्कोडर चलाना चाहूंगा । क्या यह संभव है:

  1. कमांड लाइन शेल आउटपुट प्राप्त करने के लिए, और इसे मेरे टेक्स्ट बॉक्स पर लिखें?
  2. समय बीतने के साथ एक प्रगति बार दिखाने के लिए संख्यात्मक मान प्राप्त करने के लिए?

जवाबों:


458

जब आप उचित रूप से अपना Processऑब्जेक्ट सेट बनाते हैं StartInfo:

var proc = new Process 
{
    StartInfo = new ProcessStartInfo
    {
        FileName = "program.exe",
        Arguments = "command line arguments to your executable",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

फिर प्रक्रिया शुरू करें और इसे पढ़ें:

proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
    string line = proc.StandardOutput.ReadLine();
    // do something with line
}

आप स्ट्रिंग्स को संख्यात्मक मानों में परिवर्तित करने int.Parse()या उपयोग int.TryParse()करने के लिए कर सकते हैं । यदि आप पढ़े गए तार में अमान्य संख्यात्मक वर्ण हैं, तो आपको पहले कुछ स्ट्रिंग हेरफेर करना पड़ सकता है।


4
मैं सोच रहा था कि आप StandardError से कैसे निपट सकते हैं? BTW मुझे वास्तव में यह कोड स्निपेट पसंद है! अच्छा और साफ।
कोडिया

3
धन्यवाद, लेकिन मुझे लगता है कि मैं स्पष्ट नहीं था: क्या मुझे ऐसा करने के लिए एक और लूप जोड़ना चाहिए?
कोडिया

@ कोडिया - मैं देख रहा हूं। आप एक लूप बना सकते हैं जो समाप्त हो जाता है जब दोनों धाराएं ईओएफ तक पहुंचती हैं। यह थोड़ा जटिल हो सकता है क्योंकि एक स्ट्रीम अनिवार्य रूप से ईओएफ को पहले हिट कर देगी और आप अब इससे पढ़ना नहीं चाहते हैं। आप दो छोरों को दो अलग-अलग थ्रेड में भी उपयोग कर सकते हैं।
फारुशियो

1
क्या यह तब तक पढ़ने के लिए अधिक मजबूत है जब तक कि प्रक्रिया स्वयं समाप्त न हो जाए, बल्कि धाराओं के अंत की प्रतीक्षा करें?
गुसादोर

@Gusdor - मुझे ऐसा नहीं लगता। जब प्रक्रिया समाप्त हो जाती है, तो इसकी धाराएं स्वतः बंद हो जाएंगी। इसके अलावा, एक प्रक्रिया समाप्त होने से बहुत पहले अपनी धाराओं को बंद कर सकती है।
फेरुचियो

254

आप अपने आउटपुट को सिंक्रोनाइज़ या एसिंक्रोनस तरीके से प्रोसेस कर सकते हैं ।

1. तुल्यकालिक उदाहरण

static void runCommand()
{
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR"; // Note the /c command (*)
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.Start();
    //* Read the output (or the error)
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    string err = process.StandardError.ReadToEnd();
    Console.WriteLine(err);
    process.WaitForExit();
}

ध्यान दें कि आउटपुट और त्रुटियों दोनों को संसाधित करना बेहतर है : उन्हें अलग से संभाला जाना चाहिए।

(*) कुछ कमांड के लिए (यहां StartInfo.Arguments) आपको /c निर्देश जोड़ना होगा , अन्यथा प्रक्रिया में जमा हो जाती है WaitForExit()

2. अतुल्यकालिक उदाहरण

static void runCommand() 
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set your output and error (asynchronous) handlers
    process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
    process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
    //* Start process and handlers
    process.Start();
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    process.WaitForExit();
}

static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

यदि आपको आउटपुट के साथ जटिल ऑपरेशन करने की आवश्यकता नहीं है, तो आप आउटपुटहैंडलर विधि को बायपास कर सकते हैं, बस उपलब्ध हैंडलर को जोड़ सकते हैं:

//* Set your output and error (asynchronous) handlers
process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);

2
प्यार async होगा! मैं VB.net में इस कोड (थोड़े ट्रांसक्रिप्शन के साथ) का उपयोग करने में सक्षम था
रिचर्ड बार्कर

नोट 'स्ट्रिंग आउटपुट = प्रक्रिया। मानकअनुसूचित। रीडऑन्ड ();) अगर उत्पादन की कई लाइनें हैं, तो एक बड़ी स्ट्रिंग का उत्पादन हो सकता है; Async उदाहरण, और Ferruccio से उत्तर दोनों आउटपुट लाइन को लाइन द्वारा संसाधित करते हैं।
एंड्रयू हिल

5
नोट: आपका पहला (तुल्यकालिक) दृष्टिकोण सही नहीं है! आपको StandardOutput और StandardError दोनों को सिंक्रोनाइज़ नहीं करना चाहिए! यह मृत-ताले का कारण बनेगा। कम से कम उनमें से एक async होना चाहिए।
एस। सेरपोशन

6
Process.WaitForExit () थ्रेड ब्लॉकिंग है, इस प्रकार सिंक्रोनस है। उत्तर की बात नहीं है, लेकिन मुझे लगा कि मैं इसे जोड़ सकता हूं। प्रक्रिया जोड़ें ।EnableRaisingEvents = सही है और पूरी तरह से अतुल्यकालिक होने के लिए निकास घटना का उपयोग करें।
टॉम

क्या प्रत्यक्ष रूप से पुनर्निर्देशित करना संभव नहीं है? मैं सैस आउटपुट के सभी रंगीकरण का उपयोग करता हूं?
ini

14

ठीक है, जो कोई भी चाहता है कि त्रुटियां और आउटपुट दोनों पढ़ते हैं, लेकिन किसी भी समाधान के साथ गतिरोध हो जाता है , बशर्ते कि अन्य उत्तर (जैसे मेरे), यहां एक समाधान है जिसे मैंने StandardOutputसंपत्ति के लिए एमएसडीएन स्पष्टीकरण पढ़ने के बाद बनाया है ।

उत्तर T30 के कोड पर आधारित है:

static void runCommand()
{
    //* Create your Process
    Process process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.Arguments = "/c DIR";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    //* Set ONLY ONE handler here.
    process.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
    //* Start process
    process.Start();
    //* Read one element asynchronously
    process.BeginErrorReadLine();
    //* Read the other one synchronously
    string output = process.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    process.WaitForExit();
}

static void ErrorOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{
    //* Do your stuff with the output (write to console/log/StringBuilder)
    Console.WriteLine(outLine.Data);
}

इसे जोड़ने के लिए धन्यवाद। क्या मैं पूछ सकता हूं कि आप किस कमांड का उपयोग कर रहे थे?
T30

मैं c # में एक ऐप विकसित कर रहा हूं जिसे mysqldump.exe लॉन्च करने के लिए डिज़ाइन किया गया है, उपयोगकर्ता को हर एक संदेश दिखाते हैं, जो ऐप उत्पन्न करता है, इसके समाप्त होने की प्रतीक्षा करें और फिर कुछ और कार्य करें। मैं समझ नहीं पा रहा हूं कि आप किस तरह की कमांड के बारे में बात कर रहे हैं? यह पूरा सवाल c # से एक प्रक्रिया शुरू करने के बारे में है।
शावक 12

1
यदि आप दो अलग-अलग हैंडलर का उपयोग करते हैं तो आपको गतिरोध नहीं मिलेगा
Ovi

आप भी उदाहरण में, आप इस प्रक्रिया को पढ़ते हैं। केवल एक बार शुरू करने के बाद ही सही। यदि आप इसे शुरू करना चाहते हैं, लेकिन कोई इसे लगातार पढ़ना चाहेगा, जबकि प्रक्रिया चल रही है, नहीं?
ओवी

7

ऐसा करने का मानक .NET तरीका प्रक्रिया ' StandardOutput स्ट्रीम ' से पढ़ना है । लिंक किए गए MSDN डॉक्स में एक उदाहरण है। इसी तरह, आप StandardError से पढ़ सकते हैं , और StandardInput पर लिख सकते हैं ।


5
  1. यहाँ वर्णित प्रक्रिया की कमांड लाइन शेल आउटपुट प्राप्त करना संभव है: http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx

  2. यह मेन्कोडर पर निर्भर करता है। यदि यह कमांड लाइन पर इस स्थिति को अलग करता है तो हाँ :)


4

आप 2 प्रक्रियाओं के माध्यम से, बाहर की जाँच करने के लिए साझा स्मृति का उपयोग कर सकते हैं MemoryMappedFile

आप मुख्य रूप से mmf"प्रक्रिया" का उपयोग करके मूल प्रक्रिया में एक मेमोरी मैप की गई फ़ाइल बना सकते हैं, तब तक दूसरी प्रक्रिया बनाएं जब तक कि यह समाप्त न हो जाए और इसे mmfउपयोग करने के लिए परिणाम लिखने दें BinaryWriter, फिर mmfमूल प्रक्रिया का उपयोग करके परिणाम पढ़ें , आप भी कर सकते हैं mmfकमांड लाइन तर्कों या हार्ड कोड का उपयोग करके नाम पास करें ।

मूल प्रक्रिया में मैप की गई फ़ाइल का उपयोग करते समय सुनिश्चित करें कि आप बच्चे की प्रक्रिया को मैप की गई फ़ाइल के परिणाम को लिखने से पहले मूल प्रक्रिया में मैप की गई फ़ाइल को छोड़ दें

उदाहरण: मूल प्रक्रिया

    private static void Main(string[] args)
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("memfile", 128))
        {
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(512);
            }

            Console.WriteLine("Starting the child process");
            // Command line args are separated by a space
            Process p = Process.Start("ChildProcess.exe", "memfile");

            Console.WriteLine("Waiting child to die");

            p.WaitForExit();
            Console.WriteLine("Child died");

            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("Result:" + reader.ReadInt32());
            }
        }
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

बाल प्रक्रिया

    private static void Main(string[] args)
    {
        Console.WriteLine("Child process started");
        string mmfName = args[0];

        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting(mmfName))
        {
            int readValue;
            using (MemoryMappedViewStream stream = mmf.CreateViewStream())
            {
                BinaryReader reader = new BinaryReader(stream);
                Console.WriteLine("child reading: " + (readValue = reader.ReadInt32()));
            }
            using (MemoryMappedViewStream input = mmf.CreateViewStream())
            {
                BinaryWriter writer = new BinaryWriter(input);
                writer.Write(readValue * 2);
            }
        }

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

इस नमूने का उपयोग करने के लिए, आपको अंदर 2 परियोजनाओं के साथ एक समाधान बनाने की आवश्यकता होगी, फिर आप% चाइल्ड प्रक्रिया का निर्माण परिणाम% चाइल्डडेयर% / बिन / डिबग से लेते हैं और इसे% पैरेंटडायरेक्टरी% / बिन / डिबग में कॉपी करते हैं, फिर चलाते हैं। मूल परियोजना

childDirऔर parentDirectoryपीसी सौभाग्य पर अपनी परियोजनाओं के फ़ोल्डर नाम हैं :)


1

कैसे एक प्रक्रिया शुरू करने के लिए (जैसे कि एक बैट फ़ाइल, पर्ल स्क्रिप्ट, कंसोल प्रोग्राम) और इसका मानक आउटपुट विंडोज फॉर्म पर प्रदर्शित होता है:

processCaller = new ProcessCaller(this);
//processCaller.FileName = @"..\..\hello.bat";
processCaller.FileName = @"commandline.exe";
processCaller.Arguments = "";
processCaller.StdErrReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.StdOutReceived += new DataReceivedHandler(writeStreamInfo);
processCaller.Completed += new EventHandler(processCompletedOrCanceled);
processCaller.Cancelled += new EventHandler(processCompletedOrCanceled);
// processCaller.Failed += no event handler for this one, yet.

this.richTextBox1.Text = "Started function.  Please stand by.." + Environment.NewLine;

// the following function starts a process and returns immediately,
// thus allowing the form to stay responsive.
processCaller.Start();    

आप ProcessCallerइस लिंक पर पा सकते हैं : एक प्रक्रिया शुरू करना और इसका मानक आउटपुट प्रदर्शित करना


1

आप नीचे दिए गए कोड का उपयोग करके प्रक्रिया आउटपुट लॉग कर सकते हैं:

ProcessStartInfo pinfo = new ProcessStartInfo(item);
pinfo.CreateNoWindow = false;
pinfo.UseShellExecute = true;
pinfo.RedirectStandardOutput = true;
pinfo.RedirectStandardInput = true;
pinfo.RedirectStandardError = true;
pinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
var p = Process.Start(pinfo);
p.WaitForExit();
Process process = Process.Start(new ProcessStartInfo((item + '>' + item + ".txt"))
{
    UseShellExecute = false,
    RedirectStandardOutput = true
});
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
if (process.ExitCode != 0) { 
}

1

समाधान जो मेरे लिए जीत और लाइनक्स में काम करता है वह है फोलिंग

// GET api/values
        [HttpGet("cifrado/{xml}")]
        public ActionResult<IEnumerable<string>> Cifrado(String xml)
        {
            String nombreXML = DateTime.Now.ToString("ddMMyyyyhhmmss").ToString();
            String archivo = "/app/files/"+nombreXML + ".XML";
            String comando = " --armor --recipient bibankingprd@bi.com.gt  --encrypt " + archivo;
            try{
                System.IO.File.WriteAllText(archivo, xml);                
                //String comando = "C:\\GnuPG\\bin\\gpg.exe --recipient licorera@local.com --armor --encrypt C:\\Users\\Administrador\\Documents\\pruebas\\nuevo.xml ";
                ProcessStartInfo startInfo = new ProcessStartInfo() {FileName = "/usr/bin/gpg",  Arguments = comando }; 
                Process proc = new Process() { StartInfo = startInfo, };
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.Start();
                proc.WaitForExit();
                Console.WriteLine(proc.StandardOutput.ReadToEnd());
                return new string[] { "Archivo encriptado", archivo + " - "+ comando};
            }catch (Exception exception){
                return new string[] { archivo, "exception: "+exception.ToString() + " - "+ comando };
            }
        }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.