C#でバッチファイルを実行する


139

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

@ジョン私はそれをやった、それは問題ではない。ご協力ありがとうございます。
ウェッセルT.

バッチファイルの何かが失敗していますか?プロセスにWorkingDirectory(またはそのプロパティが呼び出されるもの)を設定することができます。
ジョナス

まあ、私がコマンドでコードを手動で実行すると(スタート->実行)、正しく実行されます。私は今WORKINGDIRECTORYを追加し、SYSTEM32に設定し、私はまだのErrorCodeを取得しました:1
ヴェッセルT.

回答:


191

これはうまくいくはずです。何が起こっているのかを知るために、出力ストリームとエラーストリームの内容をダンプしてみることができます。

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ディレクトリに保持することはお勧めできません。

* * EDIT 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を実行すると、完全に実行されるためです。
ウェッセルT.

2
私はあなたの問題を再現することができました、答えへの追加をチェックしてください。
シュタイナー、2011

27 GBのデータベースをダンプファイルにダンプする「pg_dump ...> dumpfile」を実行する場合、このアプローチは適用されません
Paul

蓄積を回避するために標準出力/エラーからデータを取得するにはどうすればよいですか(バッチを何年も実行でき、データをそのまま表示したい場合)
Dani

非同期読み取りメソッド(編集2を参照)を使用すると、行が読み取られるとすぐにテキストを出力できます。
シュタイナー2015

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

この単純な行は、バッチファイルを実行します。


3
パラメータを渡し、コマンド実行の結果を読み取るにはどうすればよいですか?
Janatbek Sharsheyev 2017

@JanatbekSharsheyev表示された場合は、あなたがこれです...を求める
それは私ではなかった

1
@JanatbekSharsheyev引数として渡すことができます。以下の例を参照してください。ProcessStartInfo info = new ProcessStartInfo( "c:\\ batchfilename.bat"); info.Arguments = "-parameter"; Process.Start(info)
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
私の場合、バッチファイルはを使用して別のバッチファイルを呼び出していました~%dp0ProcessInfo.WorkingDirectory修正したそれを追加します。
ソナタ

1
commandBATファイルを直接呼び出す場合は、なぜaを渡すのですか?
sfarbota 14

@sfarbota BATファイルの引数?
sigod 2015年

@sigodあなたが私に質問しているか、私の考えられる答えを提案しているかはわかりません。はい、バッチファイルは引数を取ることができます。ただし、commandパラメーターがBATファイルに引数を送信するために使用される可能性があることを示唆している場合、それはここのコードでは示されていません。実際にはまったく使用されていません。もしそうなら、おそらくarguments代わりに名前を付けるべきです。
sfarbota 2015

@sfarbotaそれは仮定でした。ちなみに、呼び出しcommandで使用されnew ProcessStartInfoます。
sigod

13

正常に動作します。私はそれを次のようにテストしました:

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

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

ウィンドウをオフにしてコメントアウトし、実行できるようにしました。


最初に混乱を招くいくつかの点を明らかにした例をありがとう。前の例を再利用可能なメソッドに変換するには、いくつかの追加の手順が必要です。また、前の例の "string command"パラメーターは、渡されるものであるため、argsまたはparametersという名前にしておく必要があります。
Developer63

7

この質問に答えるために2つのパラメーターをbat / cmdファイルに送信しているサンプルc#コードは次のとおりですです。

コメント:パラメータを渡し、コマンド実行の結果を読み取るにはどうすればよいですか?

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

FileNameにWHOLEパスを割り当てて機能させる必要がありました(WorkingDirectoryのルートパスが同じ場合でも…)。ルートパスをスキップすると、そのようなファイルがないという例外が発生します
Hawlett

パスを確認し、何を構成しているかを確認し、パスが存在するか、手動でないかを確認します。それは問題を理解するのに役立ちます。
アンジャンカント

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

FileNameにWHOLEパスを割り当てて機能させる必要がありました(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)。個別にbatファイルを手動で実行するための追加の手順にうんざりしており、C#テストセットアップコードの一部としてbatファイルを実行したいだけでした。環境リセットバッチファイルは、テストケースファイルを入力フォルダーに戻し、出力フォルダーをクリーンアップして、テストのための適切なテスト開始状態に到達します。QuotesAroundメソッドは、フォルダー名にスペースが含まれている場合に備えて、コマンドラインを単に引用符で囲みます(「Program Files」など)。その中にあるのは次のとおりです。プライベート文字列QuotesAround(string input){return "\" "+ input +" \ "";}

あなたのシナリオが私のシナリオに似ている場合、一部の人がこれが便利で、数分節約できることを願っています。


0

以前に提案されたソリューションでは、複数のnpmコマンドをループで実行し、すべての出力をコンソールウィンドウに表示するのに苦労しました。

以前のコメントからすべてを組み合わせた後、ようやく機能し始めましたが、コード実行フローを再配置しました。

私が気づいたのは、イベントのサブスクライブが(プロセスがすでに開始した後で)遅すぎたため、一部の出力がキャプチャされなかったことです。

以下のコードは次のことを行います:

  1. プロセスが開始する前にイベントをサブスクライブするため、出力が失われることはありません。
  2. プロセスが開始されるとすぐに、出力からの読み取りを開始します。

コードはデッドロックに対してテストされていますが、同期(一度に1つのプロセス実行)であるため、これを並行して実行するとどうなるかを保証できません。

    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の使用:

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

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

-1

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

これがバッチファイルとパラメーターで機能することはわかっていますが、C#で結果を取得する方法はわかりません。通常、出力はバッチファイルで定義されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.