Thực hiện hàng loạt tệp trong C #


139

Tôi đang cố gắng thực thi một tệp bó trong C #, nhưng tôi không gặp may mắn khi làm điều đó.

Tôi đã tìm thấy nhiều ví dụ trên Internet, nhưng nó không hoạt động với tôi.

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

Chuỗi lệnh chứa tên của tệp bó (được lưu trữ system32) và một số tệp cần thao tác. (Ví dụ txtmanipulator file1.txt file2.txt file3.txt:). Khi tôi thực hiện tệp bó theo cách thủ công, nó hoạt động chính xác.

Khi thực thi mã, nó cho tôi một **ExitCode: 1** (Catch all for general errors)

Tôi đang làm gì sai?


4
Bạn không hiển thị những gì command. Nếu nó chứa các đường dẫn có khoảng trắng, bạn sẽ cần đặt dấu ngoặc kép xung quanh chúng.
Jon

@ Tôi đã làm điều đó, đó không phải là vấn đề. Cảm ơn vì đầu vào của bạn!
Tàu T.

Có một cái gì đó trong tập tin hàng loạt của bạn thất bại? Bạn có thể muốn đặt WorkDirectory (hoặc bất cứ thứ gì thuộc tính đó được gọi) cho quy trình của bạn.
Jonas

Chà, khi tôi thực thi mã theo cách thủ công (Bắt đầu -> Chạy) thì nó chạy đúng. Tôi đã thêm WorkDirectory ngay bây giờ và đặt nó vào system32, nhưng tôi vẫn nhận được ErrorCode: 1
W Tàu T.

Câu trả lời:


191

Điều này nên làm việc. Bạn có thể cố gắng loại bỏ nội dung của các luồng đầu ra và lỗi để tìm hiểu điều gì đang xảy ra:

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

* BIÊN TẬP *

Đưa ra thông tin bổ sung trong bình luận của bạn dưới đây, tôi đã có thể tạo lại vấn đề. Dường như có một số cài đặt bảo mật dẫn đến hành vi này (chưa điều tra chi tiết về điều đó).

Điều này không hoạt động nếu tập tin bó không được đặt trong C:\Windows\System32. Hãy thử di chuyển nó đến một số vị trí khác, ví dụ như vị trí thực thi của bạn. Lưu ý rằng việc giữ các tệp bó tùy chỉnh hoặc tệp thực thi trong thư mục Windows là cách thực hành tồi.

* EDIT 2 *quay ra rằng nếu các luồng được đọc đồng bộ, một bế tắc có thể xảy ra, hoặc là bằng cách đọc đồng bộ trước WaitForExithoặc bằng cách đọc cả hai stderrstdoutđồng bộ một sau khi khác.

Điều này không nên xảy ra nếu sử dụng các phương thức đọc không đồng bộ thay vào đó, như trong ví dụ sau:

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
Cảm ơn! bây giờ tôi thực sự có thể thấy những gì là lỗi. "C: \ Windows \ System32 \ txtmanipulator.bat không được nhận dạng là một lệnh nội bộ hoặc bên ngoài, chương trình hoặc batchfile" (Dịch từ tiếng Hà Lan) Đó là số lẻ. Bởi vì khi tôi chạy txtmanipulator từ dòng lệnh, nó sẽ thực thi hoàn hảo.
Tàu T.

2
Tôi đã có thể tạo lại vấn đề của bạn, kiểm tra bổ sung cho câu trả lời.
steinar

Cách tiếp cận này không áp dụng khi tôi chạy "pg_dump ...> dumpfile", kết xuất cơ sở dữ liệu 27 GB thành dumpfile
Paul

Làm cách nào tôi có thể lấy dữ liệu từ đầu ra / lỗi tiêu chuẩn để tránh tích lũy (do lô có thể chạy trong nhiều năm và tôi muốn xem dữ liệu khi nó xuất hiện?)
Dani

Sử dụng các phương thức đọc không đồng bộ (xem chỉnh sửa 2) sẽ cho phép bạn xuất văn bản ngay khi một dòng đã được đọc.
steinar

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

dòng đơn giản này sẽ thực thi các tập tin bó.


3
Làm thế nào tôi có thể truyền tham số và đọc kết quả thực hiện lệnh?
Janatbek Sharsheyev

@JanatbekSharsheyev Xem nếu đây là bạn yêu cầu ...
Đó không phải là tôi

1
@JanatbekSharsheyev bạn có thể chuyển qua làm đối số .. Xem ví dụ bên dưới ví dụ về thông tin ProcessStartInfo = new ProcessStartInfo ("c: \\ batchfilename.bat"); thông tin.Argument = "-parameter"; Process.Start (thông tin)
sk1007

17

Sau một số trợ giúp tuyệt vời từ steinar, đây là những gì làm việc cho tôi:

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
Trong trường hợp của tôi, một tệp bó đã gọi một tệp bó khác bằng cách sử dụng ~%dp0. Thêm ProcessInfo.WorkingDirectorycố định nó.
Sonata

1
Tại sao vượt qua commandnếu bạn đang gọi trực tiếp tệp BAT?
sfarbota

@sfarbota Đối số cho tệp BAT?
sigod

@sigod Tôi không chắc chắn nếu bạn hỏi tôi một câu hỏi hoặc gợi ý một câu trả lời có thể cho tôi. Có, các tệp bó có thể lấy đối số. Nhưng nếu bạn đề xuất rằng các commandtham số có thể được sử dụng để gửi đối số đến tệp BAT, thì đó không phải là những gì mã ở đây hiển thị. Nó không được sử dụng trong thực tế. Và nếu có, nó có lẽ nên được đặt tên argumentsthay thế.
sfarbota

@sfarbota Đó là một giả định. Nhân tiện, commandđược sử dụng trong new ProcessStartInfocuộc gọi.
sigod

13

Nó hoạt động tốt. Tôi đã thử nó như thế này:

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

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

Tôi nhận xét tắt cửa sổ để tôi có thể XEM nó chạy.


Cảm ơn bạn cho ví dụ đã làm rõ một vài điểm khó hiểu ban đầu. Phải mất thêm một vài bước để biến các ví dụ trước thành phương thức có thể sử dụng lại và tham số "lệnh chuỗi" trong các ví dụ trước phải được đặt tên là args hoặc tham số, vì đó là những gì được truyền vào nó.
Nhà phát

7

Dưới đây là mẫu mã c # đang gửi 2 tham số đến tệp bat / cmd để trả lời câu hỏi này .

Nhận xét: làm thế nào tôi có thể truyền tham số và đọc kết quả thực hiện lệnh?

/ bởi @Janatbek Sharsheyev

Tùy chọn 1: Không ẩn cửa sổ giao diện điều khiển, truyền đối số và không nhận kết quả đầu ra

using System;
using System.Diagnostics;


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

Tùy chọn 2: Ẩn cửa sổ giao diện điều khiển, truyền đối số và nhận đầu ra


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

Mã dưới đây làm việc tốt cho tôi

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

Tôi cần gán đường dẫn WHOLE trong FileName để làm cho nó hoạt động (ngay cả khi WorkDirectory có cùng đường dẫn gốc). Nếu tôi bỏ qua đường dẫn gốc, tôi nhận được ngoại lệ rằng không có tệp nào như vậy
Hawlett

Kiểm tra đường dẫn, những gì đang soạn thảo và kiểm tra nó, có tồn tại hay không bằng tay. Nó sẽ giúp tìm ra vấn đề.
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());
    }
}

Tôi cần gán đường dẫn WHOLE trong FileName để làm cho nó hoạt động (ngay cả khi WorkDirectory có cùng đường dẫn gốc). Nếu tôi bỏ qua đường dẫn gốc, tôi nhận được ngoại lệ rằng không có tệp nào như vậy
Hawlett

1

Bạn đã thử bắt đầu nó như một quản trị viên? Bắt đầu Visual Studio với tư cách quản trị viên nếu bạn sử dụng nó, bởi vì làm việc với .batcác tệp yêu cầu các đặc quyền đó.


0

Tôi muốn một cái gì đó có thể sử dụng trực tiếp hơn mà không cần các giá trị chuỗi mã hóa cụ thể của tổ chức trong đó. Tôi cung cấp sau đây như một đoạn mã có thể tái sử dụng trực tiếp. Nhược điểm nhỏ là cần xác định và chuyển thư mục làm việc khi thực hiện cuộc gọi.

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

Được gọi như thế này:

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

Trong ví dụ này, từ bên trong Visual Studio 2017, là một phần của quá trình chạy thử, tôi muốn chạy tệp bó thiết lập lại môi trường trước khi thực hiện một số thử nghiệm. (SpecFlow + xUnit). Tôi cảm thấy mệt mỏi với các bước bổ sung để tự chạy riêng tập tin bat và muốn chạy tập tin bat như một phần của mã thiết lập thử nghiệm C #. Tệp bó thiết lập lại môi trường di chuyển các tệp trường hợp kiểm tra trở lại thư mục đầu vào, dọn sạch các thư mục đầu ra, v.v. để đến trạng thái bắt đầu kiểm tra thích hợp để kiểm tra. Phương thức Báo giá đơn giản chỉ cần đặt dấu ngoặc kép xung quanh dòng lệnh trong trường hợp có khoảng trắng trong tên thư mục ("Tệp chương trình", có ai không?). Tất cả những gì có trong đó là: chuỗi riêng tư Báo giá chuỗi (đầu vào chuỗi) {return "\" "+ input +" \ "";}

Hy vọng một số tìm thấy điều này hữu ích và tiết kiệm một vài phút nếu kịch bản của bạn tương tự như của tôi.


0

Với các giải pháp được đề xuất trước đây, tôi đã đấu tranh để có nhiều lệnh npm được thực thi trong một vòng lặp và nhận tất cả các kết quả đầu ra trên cửa sổ giao diện điều khiển.

Cuối cùng nó đã bắt đầu hoạt động sau khi tôi kết hợp mọi thứ từ các bình luận trước đó, nhưng sắp xếp lại luồng thực thi mã.

Điều tôi nhận thấy là đăng ký sự kiện đã được thực hiện quá muộn (sau khi quá trình đã bắt đầu) và do đó một số kết quả đầu ra không được nắm bắt.

Các mã dưới đây bây giờ làm như sau:

  1. Đăng ký vào các sự kiện, trước khi quá trình bắt đầu, do đó đảm bảo rằng không có đầu ra nào bị bỏ lỡ.
  2. Bắt đầu đọc từ đầu ra ngay khi quá trình được bắt đầu.

Mã đã được kiểm tra chống lại các bế tắc, mặc dù nó là đồng bộ (thực thi một quy trình tại thời điểm đó) vì vậy tôi không thể đảm bảo điều gì sẽ xảy ra nếu điều này được chạy song song.

    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

Sử dụng CliWrap :

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

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

-1

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

Tôi biết điều này sẽ làm việc cho tệp bó và tham số, nhưng không biết làm thế nào để có kết quả trong C #. Thông thường, các đầu ra được xác định trong tệp bó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.