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

@Jon 나는 그렇게 했어, 그건 문제가 아니다. 입력 해 주셔서 감사합니다!
Wessel T.

배치 파일에 문제가 있습니까? 프로세스에 WorkingDirectory (또는 해당 속성이 호출되는 모든 것)를 설정할 수 있습니다.
Jonas

글쎄, 명령에서 수동으로 코드를 실행하면 (시작-> 실행) 올바르게 실행됩니다. 지금 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 EDIT 그것은 밝혀 전에 동기식 판독하여 하나의 스트림을 동 기적으로 판독되는 경우에 교착 상태를 초래할 수 WaitForExit또는 모두 읽어 stderrstdout다른 동기 후에 하나.

다음 예제와 같이 비동기 읽기 메소드를 대신 사용하는 경우에는 발생하지 않아야합니다.

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를 실행하면 완벽하게 실행되기 때문입니다.
Wessel T.

2
문제를 재현 할 수 있었으며 답변에 추가 사항을 확인하십시오.
steinar

27GB 데이터베이스를 덤프 파일로 덤프하는 "pg_dump ...> dumpfile"을 실행할 때는이 방법을 사용할 수 없습니다.
Paul

어떻게 축적 방지하기 위해 표준 출력 / 오류에서 데이터를 잡아 (년 동안 실행할 수있는 배치 주어 나는 그것이 온다 된 데이터를 보려면?) 수
대니

비동기 읽기 방법 (편집 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 = "-매개 ​​변수"; 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
제 경우에는 배치 파일이을 사용하여 다른 배치 파일을 호출했습니다 ~%dp0. ProcessInfo.WorkingDirectory고정 추가 .
Sonata

1
commandBAT 파일을 직접 호출 하는 경우 왜 a를 전달 합니까?
sfarbota

BAT 파일에 대한 @sfarbota 인수?
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;

나는 창문을 끄고 주석을 달아서 그것을 볼 수 있다고 언급했다.


처음에 몇 가지 혼란스러운 점을 분명히 한 예에 감사드립니다. 이전 예제를 재사용 가능한 메소드로 변환하는 데 몇 가지 추가 단계가 필요하며 이전 예제의 "string command"매개 변수는 전달되는 매개 변수로 args 또는 parameter로 지정되어야합니다.
개발자

7

다음은이 질문에 대한 답변을 위해 bat / cmd 파일에 2 개의 매개 변수를 보내는 샘플 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

구성중인 경로를 확인하고 기존 경로를 확인하십시오. 문제를 파악하는 데 도움이됩니다.
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());
    }
}

FileName에 WHOLE 경로를 지정하여 작동하게 만들었습니다 (WorkingDirectory의 루트 경로가 동일한 경우에도). 루트 경로를 건너 뛰면 그러한 파일이 없다는 예외가 발생합니다.
Hawlett

1

관리자로 시작해 보셨습니까? .bat파일 작업 에는 이러한 권한이 필요 하므로 Visual Studio를 사용하는 경우 관리자로 Visual Studio를 시작하십시오 .


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", anyone?). 개인 문자열 QuotesAround (string input) {return "\" "+ input +"\ "";}

시나리오가 내 것과 비슷한 경우 일부 사람들이 이것이 유용하고 몇 분을 절약하기를 바랍니다.


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

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.