การดำเนินการแบทช์ไฟล์ใน C #


140

ฉันพยายามรันไฟล์แบตช์ใน C # แต่ฉันไม่ได้รับโชคทำ

ฉันพบตัวอย่างหลายอย่างบนอินเทอร์เน็ต แต่มันไม่ได้ผลสำหรับฉัน

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

สตริงคำสั่งมีชื่อไฟล์แบตช์ (เก็บไว้ในsystem32) และไฟล์บางไฟล์ที่ควรจัดการ (ตัวอย่าง: txtmanipulator file1.txt file2.txt file3.txt) เมื่อฉันรันไฟล์แบตช์ด้วยตนเองมันทำงานได้อย่างถูกต้อง

เมื่อรันโค้ดมันให้ฉัน **ExitCode: 1** (Catch all for general errors)

ผมทำอะไรผิดหรือเปล่า?


4
คุณไม่แสดงว่าcommandเป็นอะไร หากมีเส้นทางที่มีช่องว่างคุณจะต้องใส่เครื่องหมายคำพูดล้อมรอบ
Jon

@ จอนฉันได้ทำแล้วนั่นไม่ใช่ปัญหา ขอบคุณสำหรับข้อมูลของคุณ!
Wessel T.

มีบางอย่างในไฟล์แบทช์ของคุณล้มเหลวหรือไม่ คุณอาจต้องการตั้งค่า WorkingDirectory (หรือสิ่งที่เรียกว่าคุณสมบัติ) สำหรับกระบวนการของคุณ
Jonas

ดีเมื่อฉันรันโค้ดในคำสั่งด้วยตนเอง (Start -> Run) มันทำงานได้อย่างถูกต้อง ฉันได้เพิ่ม WorkingDirectory แล้วและตั้งเป็น system32 แต่ฉันยังคงได้รับ ErrorCode: 1
Wessel T.

คำตอบ:


192

สิ่งนี้น่าจะใช้ได้ คุณสามารถลองทิ้งเนื้อหาของเอาต์พุตและสตรีมข้อผิดพลาดเพื่อค้นหาว่าเกิดอะไรขึ้น:

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

* แก้ไข *

เมื่อได้รับข้อมูลเพิ่มเติมในความคิดเห็นของคุณด้านล่างฉันสามารถสร้างปัญหาขึ้นมาใหม่ได้ ดูเหมือนว่าจะมีการตั้งค่าความปลอดภัยบางอย่างที่ส่งผลให้เกิดพฤติกรรมนี้ (ยังไม่ได้ตรวจสอบโดยละเอียด)

นี้ไม่C:\Windows\System32ทำงานถ้าแฟ้มชุดไม่ได้อยู่ใน ลองย้ายไปยังตำแหน่งอื่นเช่นตำแหน่งที่สามารถใช้งานได้ของคุณ โปรดทราบว่าการรักษาไฟล์แบตช์ที่กำหนดเองหรือไฟล์ปฏิบัติการในไดเรกทอรี Windows นั้นเป็นวิธีปฏิบัติที่ไม่ดีอยู่ดี

* * * * * * * * แก้ไข 2 มันกลับกลายเป็นว่าถ้าลำธารจะอ่านพร้อมการหยุดชะงักสามารถเกิดขึ้นได้ทั้งจากการอ่านพร้อมกันก่อนWaitForExitหรือโดยการอ่านทั้งสองstderrและstdoutพร้อมกันหนึ่งหลังจากที่อื่น

สิ่งนี้ไม่ควรเกิดขึ้นหากใช้วิธีการอ่านแบบอะซิงโครนัสแทนเช่นในตัวอย่างต่อไปนี้:

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: \ Windows \ System32 \ txtmanipulator.bat ไม่ได้รับการยอมรับว่าเป็นคำสั่งโปรแกรมหรือชุดไฟล์ภายในหรือภายนอก" (แปลจากภาษาดัตช์) ซึ่งแปลก เพราะเมื่อฉันรัน txtmanipulator จาก commandline มันจะทำงานได้อย่างสมบูรณ์แบบ
Wessel T.

2
ฉันสามารถสร้างปัญหาของคุณใหม่ลองอ่านคำตอบเพิ่มเติม
steinar

วิธีนี้ไม่สามารถใช้ได้เมื่อฉันเรียกใช้ "pg_dump ... > dumpfile" ซึ่งทิ้งฐานข้อมูล 27 GB ไปยัง dumpfile
Paul

ฉันจะคว้าข้อมูลจากเอาต์พุตมาตรฐาน / ข้อผิดพลาดเพื่อหลีกเลี่ยงการสะสมได้อย่างไร (เนื่องจากแบตช์สามารถทำงานได้หลายปีและฉันต้องการดูข้อมูลตามที่มา)
Dani

การใช้วิธีอ่านแบบอะซิงโครนัส (ดูแก้ไข 2) จะช่วยให้คุณสามารถส่งออกข้อความได้ทันทีที่อ่านบรรทัด
steinar

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

บรรทัดแบบง่ายนี้จะดำเนินการแบตช์ไฟล์


3
ฉันจะส่งพารามิเตอร์และอ่านผลลัพธ์ของการดำเนินการคำสั่งได้อย่างไร
Janatbek Sharsheyev

@JanatbekSharsheyev ดูว่านี่เป็นสิ่งที่คุณขอหรือไม่ ...
มันไม่ใช่ฉัน

1
@JanatbekSharsheyev คุณสามารถผ่านเป็นอาร์กิวเมนต์ .. ดูตัวอย่าง ProcessStartInfo info = new ProcessStartInfo ใหม่ ("c: \\ batchfilename.bat"); info.Arguments = "- พารามิเตอร์"; กระบวนการเริ่มต้น (ข้อมูล)
sk1007

17

หลังจากได้รับความช่วยเหลือจาก steinar นี่คือสิ่งที่ได้ผลสำหรับฉัน:

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
~%dp0ในกรณีของฉันไฟล์ชุดได้รับการเรียกไฟล์ชุดอื่นที่ใช้ การเพิ่มการProcessInfo.WorkingDirectoryแก้ไขมัน
Sonata

1
ส่งต่อทำไมcommandหากคุณกำลังเรียกไฟล์ BAT โดยตรง
sfarbota

@sfarbota อาร์กิวเมนต์สำหรับไฟล์ BAT หรือไม่
sigod

@sigod ฉันไม่แน่ใจว่าคุณกำลังถามคำถามหรือเสนอแนะคำตอบที่เป็นไปได้สำหรับฉัน ใช่ไฟล์แบตช์สามารถรับข้อโต้แย้งได้ แต่ถ้าคุณแนะนำว่าcommandพารามิเตอร์อาจใช้สำหรับส่งอาร์กิวเมนต์ไปยังไฟล์ BAT นั่นไม่ใช่สิ่งที่รหัสที่นี่แสดงให้เห็น มันไม่ได้ใช้เลยในความเป็นจริง และถ้าเป็นก็ควรจะตั้งชื่อargumentsแทน
sfarbota

@sfarbota มันเป็นสมมติฐาน โดยวิธีการที่commandใช้ในการnew ProcessStartInfoโทร
sigod

13

มันใช้งานได้ดี ฉันทดสอบแบบนี้:

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

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

ฉันแสดงความคิดเห็นว่าปิดหน้าต่างเพื่อที่ฉันจะได้เห็นมันทำงาน


ขอบคุณสำหรับตัวอย่างที่ชี้แจงจุดเริ่มต้นที่ทำให้คู่สับสน ใช้ขั้นตอนพิเศษไม่กี่ขั้นตอนในการเปลี่ยนตัวอย่างก่อนหน้านี้เป็นวิธีที่นำมาใช้ซ้ำได้และพารามิเตอร์ "คำสั่งสตริง" ในตัวอย่างก่อนหน้านี้ควรตั้งชื่อ args หรือพารามิเตอร์เนื่องจากเป็นสิ่งที่ถูกส่งผ่านไป
Developer63

7

นี่คือตัวอย่างรหัส c # ที่ส่งพารามิเตอร์ 2 ตัวไปยังไฟล์ bat / cmd เพื่อตอบคำถามนี้

ความคิดเห็น: ฉันจะส่งพารามิเตอร์และอ่านผลลัพธ์ของการดำเนินการคำสั่งได้อย่างไร

/ โดย@Janatbek Sharsheyev

ตัวเลือกที่ 1: โดยไม่ต้องซ่อนหน้าต่างคอนโซลผ่านการขัดแย้งและโดยไม่ได้รับผลลัพธ์

using System;
using System.Diagnostics;


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

ตัวเลือกที่ 2: ซ่อนหน้าต่างคอนโซลผ่านการขัดแย้งและรับผลลัพธ์


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

รหัสด้านล่างทำงานได้ดีสำหรับฉัน

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

ฉันต้องการกำหนดพา ธ WHOLE ใน FileName เพื่อให้มันทำงานได้ (แม้ว่า WorkingDirectory จะมีรูทพา ธ เดียวกัน…) หากฉันข้ามเส้นทางรูทฉันจะได้รับการยกเว้นว่าไม่มีไฟล์ดังกล่าว
Hawlett

ตรวจสอบเส้นทางสิ่งที่กำลังเขียนและตรวจสอบว่ามีอยู่หรือไม่ด้วยตนเอง มันจะช่วยให้คิดออกปัญหา
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());
    }
}

ฉันต้องการกำหนดพา ธ WHOLE ใน FileName เพื่อให้มันทำงานได้ (แม้ว่า WorkingDirectory จะมีรูทพา ธ เดียวกัน…) หากฉันข้ามเส้นทางรูทฉันจะได้รับการยกเว้นว่าไม่มีไฟล์ดังกล่าว
Hawlett

1

คุณได้ลองเริ่มเป็นผู้ดูแลระบบหรือไม่? เริ่ม Visual Studio ในฐานะผู้ดูแลระบบถ้าคุณใช้เพราะการทำงานกับ.batไฟล์ต้องใช้สิทธิ์เหล่านั้น


0

ฉันต้องการบางสิ่งที่สามารถใช้งานได้โดยตรงมากขึ้นโดยไม่ต้องใช้ค่าสตริงที่กำหนดค่าตายตัวในองค์กร ฉันเสนอต่อไปนี้เป็นรหัสที่นำมาใช้ใหม่ได้โดยตรง ข้อเสียเล็ก ๆ น้อย ๆ จะต้องตรวจสอบและส่งผ่านโฟลเดอร์การทำงานเมื่อมีการโทร

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

เรียกสิ่งนี้ว่า:

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

ในตัวอย่างนี้จากภายใน Visual Studio 2017 ซึ่งเป็นส่วนหนึ่งของการทดสอบการทำงานฉันต้องการเรียกใช้ไฟล์แบตช์รีเซ็ตสภาพแวดล้อมก่อนดำเนินการทดสอบบางอย่าง (SpecFlow + xUnit) ฉันเบื่อขั้นตอนพิเศษสำหรับการเรียกใช้ไฟล์ค้างคาวแยกกันด้วยตนเองและต้องการเพียงแค่เรียกใช้ไฟล์ค้างคาวเป็นส่วนหนึ่งของรหัสการตั้งค่าการทดสอบ C # ไฟล์แบตช์รีเซ็ตสภาพแวดล้อมจะย้ายไฟล์เคสทดสอบกลับไปที่โฟลเดอร์อินพุตล้างข้อมูลโฟลเดอร์เอาต์พุตและอื่น ๆ เพื่อไปยังสถานะเริ่มต้นการทดสอบที่เหมาะสมสำหรับการทดสอบ วิธีการ QuotesAround เพียงแค่ใส่เครื่องหมายคำพูดรอบบรรทัดคำสั่งในกรณีที่มีช่องว่างในชื่อโฟลเดอร์ ("ไฟล์โปรแกรม" ใคร?) สิ่งที่อยู่ในนั้นคือ: สตริงส่วนตัว QuotesAround (อินพุตสตริง) {ส่งคืน "\" "+ อินพุต +" \ "";}

หวังว่าบางคนพบว่าสิ่งนี้มีประโยชน์และประหยัดเวลาไม่กี่นาทีถ้าสถานการณ์ของคุณคล้ายกับของฉัน


0

ด้วยวิธีแก้ไขปัญหาที่เสนอก่อนหน้านี้ฉันได้ต่อสู้เพื่อรับคำสั่ง npm หลายคำสั่งที่ดำเนินการแบบวนซ้ำและรับเอาต์พุตทั้งหมดบนหน้าต่างคอนโซล

ในที่สุดมันก็เริ่มทำงานหลังจากที่ฉันได้รวมทุกอย่างจากความคิดเห็นก่อนหน้า แต่จัดลำดับการไหลของรหัส

สิ่งที่ฉันสังเกตเห็นคือการสมัครสมาชิกกิจกรรมนั้นสายเกินไป (หลังจากกระบวนการเริ่มต้นแล้ว) และดังนั้นผลลัพธ์บางอย่างจึงไม่ถูกบันทึก

รหัสด้านล่างนี้ทำสิ่งต่อไปนี้:

  1. สมัครสมาชิกกับเหตุการณ์ก่อนที่กระบวนการจะเริ่มต้นดังนั้นมั่นใจได้ว่าจะไม่พลาดการส่งออก
  2. เริ่มต้นการอ่านจากเอาต์พุตทันทีที่เริ่มต้นกระบวนการ

รหัสได้รับการทดสอบเทียบกับการหยุดชะงักแม้ว่ามันจะเป็นแบบซิงโครนัส (การดำเนินการหนึ่งกระบวนการในเวลานั้น) ดังนั้นฉันจึงไม่สามารถรับประกันได้ว่าจะเกิดอะไรขึ้นถ้าสิ่งนี้ทำงานแบบขนาน

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


-1

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

ฉันรู้ว่าสิ่งนี้จะทำงานกับแบตช์ไฟล์และพารามิเตอร์ แต่ไม่มีความคิดวิธีรับผลลัพธ์ใน C # โดยปกติเอาต์พุตจะถูกกำหนดในแบตช์ไฟล์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.