Menjalankan File Batch dalam C #


139

Saya mencoba menjalankan file batch dalam C #, tapi saya tidak beruntung melakukannya.

Saya telah menemukan banyak contoh di Internet yang melakukannya, tetapi tidak berhasil untuk saya.

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

String perintah berisi nama file batch (disimpan dalam system32) dan beberapa file yang harus dimanipulasi. (Contoh:) txtmanipulator file1.txt file2.txt file3.txt. Ketika saya menjalankan file batch secara manual, itu berfungsi dengan benar.

Ketika mengeksekusi kode, itu memberi saya sebuah **ExitCode: 1** (Catch all for general errors)

Apa yang saya lakukan salah?


4
Anda tidak menunjukkan apa commanditu. Jika itu berisi jalur dengan spasi, Anda harus memberi tanda kutip di sekitarnya.
Jon

@Jon saya sudah melakukan itu, itu bukan masalah. Terima kasih atas masukan Anda!
Wessel T.

Apakah ada sesuatu dalam file batch Anda yang gagal? Anda mungkin ingin mengatur DirectDirectory (atau apa pun properti itu disebut) untuk proses Anda.
Jonas

Nah, ketika saya menjalankan kode dalam perintah secara manual (Start -> Run) itu berjalan dengan benar. Saya telah menambahkan WorkingDirectory sekarang dan mengaturnya ke system32, tetapi saya masih mendapatkan ErrorCode: 1
Wessel T.

Jawaban:


191

Ini seharusnya bekerja. Anda bisa mencoba membuang konten dari output dan stream kesalahan untuk mengetahui apa yang terjadi:

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

* EDIT *

Dengan informasi tambahan dalam komentar Anda di bawah, saya dapat membuat ulang masalahnya. Tampaknya ada beberapa pengaturan keamanan yang menghasilkan perilaku ini (belum menyelidiki secara rinci).

Ini tidak bekerja jika file batch tidak terletak di C:\Windows\System32. Coba pindahkan ke lokasi lain, mis. Lokasi Anda yang bisa dieksekusi. Perhatikan bahwa menyimpan file batch kustom atau file yang dapat dieksekusi di direktori Windows adalah praktik yang buruk.

* EDIT 2 * Ini Ternyata bahwa jika aliran dibaca serentak, kebuntuan dapat terjadi, baik dengan membaca serentak sebelum WaitForExitatau dengan membaca baik stderrdan stdoutsinkron satu demi satu.

Ini seharusnya tidak terjadi jika menggunakan metode baca asinkron sebagai gantinya, seperti dalam contoh berikut:

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
Terima kasih! sekarang saya benar-benar bisa melihat apa kesalahannya. "C: \ Windows \ System32 \ txtmanipulator.bat tidak dikenali sebagai perintah, program, atau batchfile internal atau eksternal" (Diterjemahkan dari bahasa Belanda) Yang aneh. Karena ketika saya menjalankan txtmanipulator dari commandline itu dijalankan dengan sempurna.
Wessel T.

2
Saya dapat membuat kembali masalah Anda, periksa tambahan untuk jawabannya.
steinar

Pendekatan ini tidak berlaku ketika saya menjalankan "pg_dump ...> dumpfile" yang membuang basis data 27 GB ke dumpfile
Paul

Bagaimana saya bisa mengambil data dari Output standar / kesalahan untuk menghindari akumulasi (mengingat bets dapat berjalan selama bertahun-tahun dan saya ingin melihat data ketika datang?)
Dani

Menggunakan metode baca asinkron (lihat edit 2) akan memungkinkan Anda untuk mengeluarkan teks segera setelah sebuah baris dibaca.
steinar

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

baris sederhana ini akan mengeksekusi file batch.


3
bagaimana saya bisa melewati parameter dan membaca hasil dari eksekusi perintah?
Janatbek Sharsheyev

@ JanatbekSharsheyev Lihat apakah ini yang Anda minta ...
It Wasn't Me

1
@JanatbekSharsheyev Anda dapat memberikan argumen .. Lihat di bawah ini contoh info ProcessStartInfo = new ProcessStartInfo ("c: \\ batchfilename.bat"); info.Arguments = "-parameter"; Process.Start (info)
sk1007

17

Setelah bantuan besar dari steinar inilah yang bekerja untuk saya:

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
Dalam kasus saya, file batch memanggil file batch lain menggunakan ~%dp0. Menambahkan ProcessInfo.WorkingDirectorymemperbaikinya.
Sonata

1
Mengapa lewat a commandjika Anda memanggil file BAT secara langsung?
sfarbota

@sfarbota Argumen untuk file BAT?
sigod

@sigod Saya tidak yakin apakah Anda mengajukan pertanyaan kepada saya atau menyarankan kemungkinan jawaban untuk pertanyaan saya. Ya, file batch dapat mengambil argumen. Tetapi jika Anda menyarankan bahwa commandparameter mungkin digunakan untuk mengirim argumen ke file BAT, bukan itu yang ditunjukkan oleh kode di sini. Sebenarnya tidak digunakan sama sekali. Dan jika ya, mungkin harus dinamai arguments.
sfarbota

@ sfarbota Itu asumsi. By the way, commanddigunakan dalam new ProcessStartInfopanggilan.
sigod

13

Ini bekerja dengan baik. Saya mengujinya seperti ini:

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

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

Saya berkomentar mematikan jendela sehingga saya bisa MELIHAT menjalankannya.


Terima kasih atas contoh yang mengklarifikasi pasangan pada awalnya membingungkan. Dibutuhkan beberapa langkah tambahan untuk mengubah contoh sebelumnya menjadi metode yang dapat digunakan kembali, dan parameter "string perintah" dalam contoh sebelumnya seharusnya dinamai args atau parameter, karena itulah yang diteruskan di dalamnya.
Developer63

7

Berikut adalah contoh kode c # yang mengirimkan 2 parameter ke file bat / cmd untuk menjawab pertanyaan ini .

Komentar: bagaimana saya bisa melewati parameter dan membaca hasil dari eksekusi perintah?

/ dari @Janatbek Sharsheyev

Opsi 1: Tanpa menyembunyikan jendela konsol, meneruskan argumen dan tanpa mendapatkan output

using System;
using System.Diagnostics;


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

Opsi 2: Menyembunyikan jendela konsol, meneruskan argumen dan mengambil output


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

Kode di bawah ini berfungsi dengan baik untuk saya

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...");
}

Saya perlu menetapkan jalur WHOLE di FileName untuk membuatnya berfungsi (bahkan jika WorkingDirectory memiliki jalur root yang sama ...). Jika saya melewatkan jalur root saya mendapatkan pengecualian bahwa tidak ada file seperti itu
Hawlett

Periksa jalur, apa yang menyusun dan memeriksanya, apakah ada atau tidak secara manual. Ini akan membantu untuk memecahkan masalah ini.
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());
    }
}

Saya perlu menetapkan jalur WHOLE di FileName untuk membuatnya berfungsi (bahkan jika WorkingDirectory memiliki jalur root yang sama ...). Jika saya melewatkan jalur root saya mendapatkan pengecualian bahwa tidak ada file seperti itu
Hawlett

1

Sudahkah Anda mencoba memulainya sebagai administrator? Mulai Visual Studio sebagai administrator jika Anda menggunakannya, karena bekerja dengan .batfile memerlukan hak istimewa itu.


0

Saya menginginkan sesuatu yang lebih langsung dapat digunakan tanpa nilai string hard-kode khusus organisasi di dalamnya. Saya menawarkan yang berikut sebagai potongan kode yang dapat digunakan kembali secara langsung. Kelemahan minor perlu ditentukan dan dilewati folder yang berfungsi saat melakukan panggilan.

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

Disebut seperti ini:

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

Dalam contoh ini, dari dalam Visual Studio 2017, sebagai bagian dari uji coba, saya ingin menjalankan file kumpulan ulang lingkungan sebelum menjalankan beberapa tes. (SpecFlow + xUnit). Saya bosan dengan langkah-langkah tambahan untuk secara manual menjalankan file bat secara terpisah, dan ingin menjalankan file bat sebagai bagian dari kode pengaturan tes C #. File kumpulan ulang lingkungan memindahkan file kasus uji kembali ke folder input, membersihkan folder output, dll. Untuk mendapatkan status awal pengujian yang tepat untuk pengujian. Metode QuotesAround hanya menempatkan tanda kutip di sekitar baris perintah jika ada spasi dalam nama folder ("Program Files", ada orang?). Semua yang ada di dalamnya adalah ini: private string QuotesAround (input string) {return "\" "+ input +" \ "";}

Semoga beberapa menemukan ini berguna dan menghemat beberapa menit jika skenario Anda mirip dengan skenario saya.


0

Dengan solusi yang diusulkan sebelumnya, saya telah berjuang untuk mendapatkan beberapa perintah npm dieksekusi dalam satu lingkaran dan mendapatkan semua output di jendela konsol.

Akhirnya mulai bekerja setelah saya menggabungkan semuanya dari komentar sebelumnya, tetapi mengatur ulang alur eksekusi kode.

Apa yang saya perhatikan adalah bahwa acara berlangganan sudah terlambat (setelah proses sudah dimulai) dan oleh karena itu beberapa output tidak ditangkap.

Kode di bawah ini sekarang melakukan yang berikut:

  1. Berlangganan acara, sebelum proses dimulai, oleh karena itu memastikan bahwa tidak ada output yang terlewat.
  2. Mulai membaca dari output segera setelah proses dimulai.

Kode telah diuji terhadap deadlock, meskipun sinkron (satu proses eksekusi pada saat itu) jadi saya tidak dapat menjamin apa yang akan terjadi jika ini dijalankan secara paralel.

    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

Menggunakan CliWrap :

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

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

-1

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

Saya tahu ini akan berfungsi untuk file batch dan parameter, tetapi tidak ada ide cara mendapatkan hasil dalam C #. Biasanya, output didefinisikan dalam file batch.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.