Toplu İş Dosyasını C # ile Yürütme


140

C # bir toplu iş dosyası yürütmek çalışıyorum, ama bunu yaparken herhangi bir şans almıyorum.

İnternette bunu yaparken birden fazla örnek buldum, ama benim için çalışmıyor.

public void ExecuteCommand(string command)
{
    int ExitCode;
    ProcessStartInfo ProcessInfo;
    Process Process;

    ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
    ProcessInfo.CreateNoWindow = true;
    ProcessInfo.UseShellExecute = false;

    Process = Process.Start(ProcessInfo);
    Process.WaitForExit();

    ExitCode = Process.ExitCode;
    Process.Close();

    MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
}

Komut dizesi, toplu iş dosyasının (içinde depolanan system32) adını ve değiştirmesi gereken bazı dosyaları içerir. (Örnek:) txtmanipulator file1.txt file2.txt file3.txt. Toplu iş dosyasını el ile yürüttüğümde, düzgün çalışıyor.

Kodu yürütürken, bana bir **ExitCode: 1** (Catch all for general errors)

Neyi yanlış yapıyorum?


4
Ne commandolduğunu göstermiyorsun. Boşluk içeren yollar içeriyorsa, etrafına tırnak işareti koymanız gerekir.
Jon

@ Bunu yaptığımda sorun değil. Girdiniz için teşekkürler!
Wessel T.

Toplu iş dosyanızdaki bir şey başarısız mı? İşleminiz için WorkingDirectory öğesini (veya bu özelliğin adı ne olursa olsun) ayarlamak isteyebilirsiniz.
Jonas

Komuttaki kodu manuel olarak yürüttüğümde (Başlat -> Çalıştır) düzgün çalışıyor. Şimdi WorkingDirectory ekledim ve system32 için ayarladım, ama yine de ErrorCode: 1
Wessel T.

Yanıtlar:


192

Bu çalışmalı. Ne olduğunu bulmak için çıktı ve hata akışlarının içeriğini boşaltmayı deneyebilirsiniz:

static void ExecuteCommand(string command)
{
    int exitCode;
    ProcessStartInfo processInfo;
    Process process;

    processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
    processInfo.CreateNoWindow = true;
    processInfo.UseShellExecute = false;
    // *** Redirect the output ***
    processInfo.RedirectStandardError = true;
    processInfo.RedirectStandardOutput = true;

    process = Process.Start(processInfo);
    process.WaitForExit();

    // *** Read the streams ***
    // Warning: This approach can lead to deadlocks, see Edit #2
    string output = process.StandardOutput.ReadToEnd();
    string error = process.StandardError.ReadToEnd();

    exitCode = process.ExitCode;

    Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
    Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
    Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
    process.Close();
}

static void Main()
{
    ExecuteCommand("echo testing");
}   

* DÜZENLE *

Aşağıdaki yorumunuzdaki ek bilgiler göz önüne alındığında, sorunu yeniden oluşturabildim. Bu davranışla sonuçlanan bir güvenlik ayarı var gibi görünüyor (bunu ayrıntılı olarak araştırmadık).

Bu does toplu iş dosyası bulunan değilse işi C:\Windows\System32. Başka bir konuma, örneğin çalıştırılabilir dosyanızın konumuna taşımayı deneyin. Özel toplu iş dosyalarını veya yürütülebilir dosyaları Windows dizininde tutmanın yine de kötü bir uygulama olduğunu unutmayın.

* * DÜZENLEME 2 O çıkıyor önce eşzamanlı okuyarak ya akışları eşzamanlı okunur bile, bir kilitlenme oluşabilir WaitForExitveya her ikisi okuyarak stderrve stdoutbirbiri ardına eşzamanlı biri.

Aşağıdaki örnekte olduğu gibi bunun yerine eşzamansız okuma yöntemleri kullanılıyorsa bu gerçekleşmemelidir:

static void ExecuteCommand(string command)
{
    var processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
    processInfo.CreateNoWindow = true;
    processInfo.UseShellExecute = false;
    processInfo.RedirectStandardError = true;
    processInfo.RedirectStandardOutput = true;

    var process = Process.Start(processInfo);

    process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
        Console.WriteLine("output>>" + e.Data);
    process.BeginOutputReadLine();

    process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
        Console.WriteLine("error>>" + e.Data);
    process.BeginErrorReadLine();

    process.WaitForExit();

    Console.WriteLine("ExitCode: {0}", process.ExitCode);
    process.Close();
}

1
Teşekkürler! şimdi aslında hatanın ne olduğunu görebiliyorum. "C: \ Windows \ System32 \ txtmanipulator.bat dahili veya harici bir komut, program veya toplu dosya olarak tanınmadı" (Hollandacadan çevrildi) Bu garip. Çünkü komut satırından txtmanipulator'ı çalıştırdığımda mükemmel bir şekilde çalışıyor.
Wessel T.

2
Sorununuzu yeniden oluşturabildim, cevaba eke baktım.
steinar

27 GB'lik bir veritabanını dump dosyasına gönderen "pg_dump ...> dumpfile" komutunu çalıştırdığımda bu yaklaşım geçerli değildir
Paul

Birikimi önlemek için standart çıktı / hatadan verileri nasıl alabilirim (parti yıllarca çalışabilir ve verileri geldiği gibi görmek istiyorum)
Dani

Eşzamansız okuma yöntemlerini (bkz. Düzenleme 2) kullanmak, bir satır okur okunmaz metin çıktısı almanızı sağlar.
steinar

132
System.Diagnostics.Process.Start("c:\\batchfilename.bat");

bu basit satır toplu iş dosyasını yürütür.


3
parametreleri nasıl iletebilir ve komut yürütme sonucunu nasıl okuyabilirim?
Janatbek Sharsheyev

@JanatbekSharsheyev bakınız eğer bu ... sormak
Beni değil miydi

1
@JanatbekSharsheyev bağımsız değişken olarak iletebilirsiniz .. Aşağıdaki örneğe bakın ProcessStartInfo info = new ProcessStartInfo ("c: \\ batchfilename.bat"); info.Arguments = "-parametre"; Process.Start (info)
sk1007

17

Steinar'dan bazı büyük yardımlardan sonra benim için işe yarayan buydu:

public void ExecuteCommand(string command)
{
    int ExitCode;
    ProcessStartInfo ProcessInfo;
    Process process;

    ProcessInfo = new ProcessStartInfo(Application.StartupPath + "\\txtmanipulator\\txtmanipulator.bat", command);
    ProcessInfo.CreateNoWindow = true;
    ProcessInfo.UseShellExecute = false;
    ProcessInfo.WorkingDirectory = Application.StartupPath + "\\txtmanipulator";
    // *** Redirect the output ***
    ProcessInfo.RedirectStandardError = true;
    ProcessInfo.RedirectStandardOutput = true;

    process = Process.Start(ProcessInfo);
    process.WaitForExit();

    // *** Read the streams ***
    string output = process.StandardOutput.ReadToEnd();
    string error = process.StandardError.ReadToEnd();

    ExitCode = process.ExitCode;

    MessageBox.Show("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
    MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
    MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
    process.Close();
}

1
Benim durumumda, bir toplu iş dosyası kullanarak başka bir toplu iş dosyası çağırıyordu ~%dp0. Ekleme ProcessInfo.WorkingDirectorydüzelttim.
Sonata

1
commandBAT dosyasını doğrudan çağırıyorsanız neden başarılı olmalısınız?
sfarbota

@sfarbota BAT dosyası için argümanlar?
sigod

@sigod Bana bir soru sorup sormadığından veya benimkine olası bir cevap önerdiğinden emin değilim. Evet, toplu iş dosyaları argüman alabilir. Ancak commandparametrelerin BAT dosyasına argüman göndermek için kullanılabileceğini düşünüyorsanız, buradaki kod bu değildir. Aslında hiç kullanılmaz. Ve eğer öyleyse, bunun argumentsyerine muhtemelen adlandırılmalıdır .
sfarbota

@sfarbota Bu bir varsayımdı. Bu arada, çağrıda commandkullanılır new ProcessStartInfo.
sigod

13

İyi çalışıyor. Ben böyle test:

String command = @"C:\Doit.bat";

ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
// ProcessInfo.CreateNoWindow = true;

Ben koşmak görmek böylece pencereyi kapatarak yorumladı.


Başlangıçta kafa karıştırıcı birkaç noktayı açıklığa kavuşturan örnek için teşekkür ederim. Önceki örnekleri yeniden kullanılabilir bir yönteme dönüştürmek için fazladan birkaç adım gerekir ve önceki örneklerde yer alan "string command" parametresi argümanlar veya parametreler olarak adlandırılmış olmalıdır;
Developer63

7

İşte bu soruyu cevaplamak için bir bat / cmd dosyasına 2 parametre gönderen örnek c # kodu .

Yorum: Parametreleri nasıl iletebilir ve komut yürütmesinin sonucunu nasıl okuyabilirim?

/ @Janatbek Sharsheyev tarafından

Seçenek 1: Konsol penceresini gizlemeden, bağımsız değişkenleri geçmeden ve çıktıları almadan

using System;
using System.Diagnostics;


namespace ConsoleApplication
{
    class Program
    { 
        static void Main(string[] args)
        {
         System.Diagnostics.Process.Start(@"c:\batchfilename.bat", "\"1st\" \"2nd\"");
        }
    }
}

2.Seçenek: Konsol penceresini gizleme, bağımsız değişkenleri iletme ve çıktı alma


using System;
using System.Diagnostics;

namespace ConsoleApplication
{
    class Program
    { 
        static void Main(string[] args)
        {
         var process = new Process();
         var startinfo = new ProcessStartInfo(@"c:\batchfilename.bat", "\"1st_arg\" \"2nd_arg\" \"3rd_arg\"");
         startinfo.RedirectStandardOutput = true;
         startinfo.UseShellExecute = false;
         process.StartInfo = startinfo;
         process.OutputDataReceived += (sender, argsx) => Console.WriteLine(argsx.Data); // do whatever processing you need to do in this handler
         process.Start();
         process.BeginOutputReadLine();
         process.WaitForExit();
        }
    }
}


3

Aşağıdaki kod benim için iyi çalıştı

using System.Diagnostics;

public void ExecuteBatFile()
{
    Process proc = null;

    string _batDir = string.Format(@"C:\");
    proc = new Process();
    proc.StartInfo.WorkingDirectory = _batDir;
    proc.StartInfo.FileName = "myfile.bat";
    proc.StartInfo.CreateNoWindow = false;
    proc.Start();
    proc.WaitForExit();
    ExitCode = proc.ExitCode;
    proc.Close();
    MessageBox.Show("Bat file executed...");
}

(WorkingDirectory aynı kök yolu olsa bile) çalışması için FileName içinde BÜTÜN yol atamak gerekiyordu. Kök yolunu
atlarsam

Ne oluşturduğunu kontrol edin ve kontrol edin, mevcut olup olmadığını kontrol edin. Sorunun anlaşılmasına yardımcı olacaktır.
Anjan Kant

2
using System.Diagnostics;

private void ExecuteBatFile()
{
    Process proc = null;
    try
    {
        string targetDir = string.Format(@"D:\mydir");   //this is where mybatch.bat lies
        proc = new Process();
        proc.StartInfo.WorkingDirectory = targetDir;
        proc.StartInfo.FileName = "lorenzo.bat";
        proc.StartInfo.Arguments = string.Format("10");  //this is argument
        proc.StartInfo.CreateNoWindow = false;
        proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;  //this is for hiding the cmd window...so execution will happen in back ground.
        proc.Start();
        proc.WaitForExit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception Occurred :{0},{1}", ex.Message, ex.StackTrace.ToString());
    }
}

(WorkingDirectory aynı kök yolu olsa bile) çalışması için FileName içinde BÜTÜN yol atamak gerekiyordu. Kök yolunu
atlarsam

1

Yönetici olarak başlatmayı denediniz mi? Visual Studio'yu kullanıyorsanız yönetici olarak başlatın, çünkü .batdosyalarla çalışmak bu ayrıcalıkları gerektirir.


0

İçinde kuruluşa özgü sabit kodlu dize değerleri olmadan daha doğrudan kullanılabilir bir şey istedim. Doğrudan yeniden kullanılabilir bir kod yığını olarak aşağıdaki sunuyoruz. Küçük dezavantajı, çağrı yaparken çalışma klasörünü belirleyip geçmesi gerekir.

public static void ExecuteCommand(string command, string workingFolder)
        {
            int ExitCode;
            ProcessStartInfo ProcessInfo;
            Process process;

            ProcessInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
            ProcessInfo.CreateNoWindow = true;
            ProcessInfo.UseShellExecute = false;
            ProcessInfo.WorkingDirectory = workingFolder;
            // *** Redirect the output ***
            ProcessInfo.RedirectStandardError = true;
            ProcessInfo.RedirectStandardOutput = true;

            process = Process.Start(ProcessInfo);
            process.WaitForExit();

            // *** Read the streams ***
            string output = process.StandardOutput.ReadToEnd();
            string error = process.StandardError.ReadToEnd();

            ExitCode = process.ExitCode;

            MessageBox.Show("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
            MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
            MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
            process.Close();
        }

Şöyle denir:

    // This will get the current WORKING directory (i.e. \bin\Debug)
    string workingDirectory = Environment.CurrentDirectory;
    // This will get the current PROJECT directory
    string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
    string commandToExecute = Path.Combine(projectDirectory, "TestSetup", "WreckersTestSetupQA.bat");
    string workingFolder = Path.GetDirectoryName(commandToExecute);
    commandToExecute = QuotesAround(commandToExecute);
    ExecuteCommand(commandToExecute, workingFolder);

Bu örnekte, Visual Studio 2017 içinden, bir test çalıştırmasının parçası olarak, bazı testleri gerçekleştirmeden önce bir ortam sıfırlama toplu iş dosyası çalıştırmak istiyorum. (SpecFlow + xUnit). Yarasa dosyasını ayrı ayrı manuel olarak çalıştırmak için ekstra adımlardan bıktım ve sadece bat dosyasını C # test kurulum kodunun bir parçası olarak çalıştırmak istedim. Ortam sıfırlama toplu dosyası, test durumu dosyalarını giriş klasörüne geri taşır, çıktı klasörlerini temizler, vb. QuotesAround yöntemi klasör adlarında boşluk olması durumunda ("Program Dosyaları", herhangi biri?) Tırnak işaretleri komut satırının etrafına koyar. İçinde tüm bunlar şu: private string QuotesAround (string input) {return "\" "+ input +" \ "";}

Umarım bazıları bunu faydalı bulur ve senaryonuz benimkine benziyorsa birkaç dakika tasarruf edin.


0

Daha önce önerilen çözümlerle, bir döngüde birden çok npm komutu yürütmek ve konsol penceresindeki tüm çıktıları almak için mücadele ettim.

Sonunda önceki yorumlardan her şeyi birleştirdikten sonra çalışmaya başladı, ancak kod yürütme akışını yeniden düzenledi.

Fark ettim, olay aboneliğinin çok geç yapıldığı (işlem başladıktan sonra) ve bu nedenle bazı çıktıların yakalanmadığı.

Aşağıdaki kod şimdi aşağıdakileri yapıyor:

  1. İşlem başlamadan önce olaylara abone olur, böylece hiçbir çıkışın kaçırılmamasını sağlar.
  2. İşlem başlar başlamaz çıktılardan okumaya başlar.

Eşzamanlı olmasına rağmen (o anda bir işlem yürütme) kod çıkmazlara karşı test edilmiştir, bu yüzden paralel olarak çalıştırılırsa ne olacağını garanti edemem.

    static void RunCommand(string command, string workingDirectory)
    {
        Process process = new Process
        {
            StartInfo = new ProcessStartInfo("cmd.exe", $"/c {command}")
            {
                WorkingDirectory = workingDirectory,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardOutput = true
            }
        };

        process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("output :: " + e.Data);

        process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("error :: " + e.Data);

        process.Start();
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();
        process.WaitForExit();

        Console.WriteLine("ExitCode: {0}", process.ExitCode);
        process.Close();
    }

0

CliWrap kullanma :

var result = await Cli.Wrap("foobar.bat").ExecuteBufferedAsync();

var exitCode = result.ExitCode;
var stdOut = result.StandardOutput;

-1

System.Diagnostics.Process.Start(BatchFileName, Parameters);

Bu toplu iş dosyası ve parametreleri için çalışacağını biliyorum, ama nasıl C # sonuçları almak için hiçbir fikir. Genellikle, çıktılar toplu iş dosyasında tanımlanır.

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.