前言
有时候会遇到使用 c# 代码是调用 bat 文件的需求。此文旨在提供一个简单的示例,以后会陆续补充响应内容。
示例
一、创建一个命令行运行程序 HelloBat
此处没有直接通过命令行打印内容,而是将当前内容添加到指定的文件中。为了调用不启动窗口也可以证明方法确实执行了。
- Main.cs
class Program
{
    static void Main(string[] args)
    {
        var nowStr = DateTime.Now.ToString()+"\n";
        string targetFilePath = "E:\\LEARN\\Socket\\HelloBat\\HelloBat\\bin\\Release\\temp.txt";
        File.AppendAllText(targetFilePath, nowStr, Encoding.UTF8); // 也可以指定编码方式 
    }
}
二、创建脚本文件
- HelloBat.bat
对上述程序编译。为对应运行程序(HelloBat.exe) 编写简单的脚本。内容如下:
call HelloBat.exe
三、测试
编写单元测试方法。(直接写一个Main()方法也可)
- Test
[TestMethod]
public void TestHelloBat()
{
    using (Process process = new Process())
    {
        string batPath = "E:\\LEARN\\Socket\\HelloBat\\HelloBat\\bin\\Release\\HelloBat.bat";
        FileInfo file = new FileInfo(batPath);
        if (file.Directory != null)
        {
            process.StartInfo.WorkingDirectory = file.Directory.FullName;
        }
        process.StartInfo.FileName = batPath;
        process.StartInfo.UseShellExecute = false;
        process.Start();
        process.WaitForExit();
    }
}
执行后可以在对应的写入文本文件(temp.txt)中查看内容。
执行指定的内容
 /// <summary>  
/// 执行cmd命令  
/// 多命令请使用批处理命令连接符:  
/// <![CDATA[ 
/// &:同时执行两个命令 
/// |:将上一个命令的输出,作为下一个命令的输入 
/// &&:当&&前的命令成功时,才执行&&后的命令 
/// ||:当||前的命令失败时,才执行||后的命令]]>  
/// 其他请百度  
/// </summary>  
/// <param name="cmd">需要执行的内容</param>  
/// <param name="output">cmd 执行结果</param>  
private static void RunCmd(string cmd, out string output)
{
    cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态  
    using (Process p = new Process())
    {
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动  
        p.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息  
        p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息  
        p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出  
        p.StartInfo.CreateNoWindow = true;          //不显示程序窗口  
        p.Start();//启动程序  
        //向cmd窗口写入命令  
        p.StandardInput.WriteLine(cmd);
        p.StandardInput.AutoFlush = true;
        //获取cmd窗口的输出信息  
        output = p.StandardOutput.ReadToEnd();
        p.WaitForExit();//等待程序执行完退出进程  
        p.Close();
    }
}  
总结
暂时写到这里。
后面会有拓展内容,如:想在程序中拿到执行的结果,或者是想传入参数等应该如何处理。
Bat 阻塞问题
这个问题很奇怪。执行 bat 时会导致线程进入假死状态。
具体原因如下:C#使用Process调用批处理阻塞问题
解决方案:
一、讨论贴
情景复现
一、执行 cmd 如下。
public void ExecuteHelpCmd(string command="help")
{
    Process p = new Process();
    p.StartInfo.FileName = "cmd.exe";
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.RedirectStandardInput = true;
    p.StartInfo.RedirectStandardOutput = true;
    
    p.Start();
    p.StandardInput.WriteLine(command);
    p.StandardInput.WriteLine("exit");
    p.WaitForExit();
    
    FileUtil.WriteLine(p.StandardOutput.ReadToEnd());
    
    p.Close();
}
其中 FileUtil 内容如下,只是简单的文件输出:
public class FileUtil
{
    /// <summary>
    /// 将文件内容写死到固定的文件中。
    /// </summary>
    /// <param name="content"></param>
    public static void WriteLine(string content)
    {
       string currentDate = DateTime.Now.ToString("yyyyMMddHHmmss"); 
       string path = @"E:\1.txt";
       File.AppendAllText(path, currentDate + content);
    }
}
调用此方法,程序会卡死。
一、简单粗暴
p.WaitForExit();
这段代码修改为
p.WaitForExit(5000);
其中等待时间单位为毫秒,到时间后会自动kill进程。(这个会不会影响比较长时间的执行,未验证。)
二、异步回显(个人推荐)
public void ExecuteHelpCmd(string command="help")
{
    Process p = new Process();
    p.StartInfo.FileName = "cmd.exe";
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.RedirectStandardInput = true;  // 重定向输入
    p.StartInfo.RedirectStandardOutput = true; // 重定向标准输出
    p.StartInfo.RedirectStandardError = true;  // 重定向错误输出 
    p.OutputDataReceived += new DataReceivedEventHandler(CmdOutputDataReceived);
    p.ErrorDataReceived += new DataReceivedEventHandler(CmdErrorDataReceived);  
    p.Start();
    p.BeginOutputReadLine();
    p.BeginErrorReadLine();  
    p.StandardInput.WriteLine(command);
    p.StandardInput.WriteLine("exit");
    p.Close();
}
/// <summary>
/// 输出回显
/// </summary>
/// <param name="sendingProcess"></param>
/// <param name="outLine"></param>
private void CmdOutputDataReceived(object sendingProcess,
    DataReceivedEventArgs outLine)
{
    // Collect the sort command output.
    if (!String.IsNullOrEmpty(outLine.Data))
    {
        //output 
        FileUtil.WriteLine(outLine.Data);
    }
}
/// <summary>
/// 错误回显
/// </summary>
/// <param name="sendingProcess"></param>
/// <param name="outLine"></param>
private void CmdErrorDataReceived(object sendingProcess,
    DataReceivedEventArgs outLine)
{
    // Collect the sort command output.
    if (!String.IsNullOrEmpty(outLine.Data))
    {
        //output 
        FileUtil.WriteLine(outLine.Data);
    }
}
