【问题标题】:Can I suppress the CR/CRLF sequence in a CMD.EXE error message?我可以在 CMD.EXE 错误消息中抑制 CR/CRLF 序列吗?
【发布时间】:2021-07-05 08:35:37
【问题描述】:

在标准 Windows 命令外壳中,尝试运行无法找到的命令会导致以下错误(逐字):

'iamanonexistentcommand' is not recognized as an internal or external command,
operable program or batch file.

... external command, 之后有什么方法可以抑制 CR/CRLF 吗?

它似乎与命令窗口的大小或错误命令的长度无关,至少在交互式 shell 中是这样。

当我在 .net 框架中使用 Process.Start 运行命令 shell 时尝试捕获并重新格式化错误消息时,这会导致我出现问题。

我最终收到了多条相同的消息,我只想捕获从一条消息到另一条消息的变化。问题是当数据可用于通过ErrorDataReceived 事件进行处理时,CR/CRLF 被解释为 EOM,因此结果是我收到了每个实际故障的多条消息,一条用于'iamanonexistentcommand' is not recognized as an internal or external command,,另一条用于operable program or batch file.

这感觉就像是为了激怒我而设计的。

【问题讨论】:

  • 我的猜测是,这是在操作系统二进制文件深处的某个地方烘焙出来的。自从恐龙在地球上漫游以来,很可能就是这样
  • 您的选择似乎是 1) 向管理此问题的 ms 团队发送反馈建议(不喜欢您的机会,他们可能住在雷德蒙德的地下室某处尼古丁染色的墙壁和鲍勃·迪伦的照片)... 2) 找到有问题的图书馆; dll注入cmd.exe;代理和改变品味... 3) 不做任何事情并建立对世界的怨恨... 4) 或者只是防御性地针对它进行编程5 ) 改变你的方法
  • @TheGeneral 无论发生什么,我都会积怨。有一些用于重复字符串检测的简单算法,所以我想我只是对整个事情进行后处理。我怀疑你是对的,这是永远不会改变的旧的旧行为。
  • @TheGeneral:这不正确,自上一个冰河时代以来就是如此。当恐龙还在附近时,消息是Bad command or file name,没有换行符——新消息是为了升级。未来 20 年我们可能还会看到改进!
  • 更严肃地说,看看 PowerShell 是否可以作为替代 shell 的选项。它不会自行运行批处理文件(它将其委托给 cmd),但它在所有最新版本的 Windows 中开箱即用,并且可以从 C# 以编程方式驱动,而无需解析文本。

标签: c# windows command-line


【解决方案1】:

尝试以下操作,看看它是否满足您的需求——它展示了如何使用System.Diagnostics.Process 处理异常,并且似乎避免了您提到的问题。它已经过测试,但它是否适用于您的情况可能取决于您正在调用的其他程序以及其他程序如何生成输出。

此外,如果您对要执行的程序使用完全限定名称(例如:C:\Windows\system32\ipconfig.exe),您可以使用System.IO.File.Exists 检查文件是否存在,在尝试使用“进程”调用它之前。

if (!System.IO.File.Exists(@"C:\Windows\system32\ipconfig.exe))
{
    System.Diagnostics.Debug.WriteLine("Error: Filename doesn't exist.");

    //ToDo: add desired code here

}

创建一个类(名称:Helper)

添加以下 using 语句:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

创建两个事件以允许将数据发送给订阅者。

//delegates
public delegate void EventHandlerProcessErrorReceived(object sender, string data);
public delegate void EventHandlerProcessOutputReceived(object sender, string data);

//event that subscribers can subscribe to
public event EventHandlerProcessErrorReceived ProcessErrorReceived;
public event EventHandlerProcessOutputReceived ProcessOutputReceived;

创建两个我们可以调用的方法将数据传递给订阅者:

  • OnErrorReceived - 收到错误消息时引发
  • OnOutputReceived - 在收到标准输出消息时引发

每个方法都会检查是否有订阅者,以及是否存在任何数据。如果是,则引发事件。

private void OnErrorReceived(string data)
{
    //only raise event if there are subscribers and actual data exists
    if (ProcessErrorReceived != null && !String.IsNullOrEmpty(data))
    {
        //raise event
        ProcessErrorReceived(this, data);
    }
}

private void OnOutputReceived(string data)
{
    //only raise event if there are subscribers and actual data exists
    if (ProcessOutputReceived != null && !String.IsNullOrEmpty(data))
    {
        //raise event
        ProcessOutputReceived(this, data);
    }
}

创建调用 System.Diagnostics.Process 的方法(名称:RunCmd)

RunCmd

public void RunCmd(string exePath, string arguments = null, Dictionary<string, string> environmentVarDict = null)
{
    string errMsg = string.Empty;

    try
    {
        if (String.IsNullOrEmpty(exePath))
        {
            errMsg = "Error: fullyQualifiedExePath not specified";

            Debug.WriteLine("Error: " + errMsg);
            OnErrorReceived(errMsg); //raise event

            return;
        }

        //create new instance
        ProcessStartInfo startInfo = new ProcessStartInfo(exePath, arguments);

        //add environment variables, if specified
        if (environmentVarDict != null)
        {
            foreach (KeyValuePair<string, string> kvp in environmentVarDict)
            {
                //add environment variable
                startInfo.EnvironmentVariables[kvp.Key] = kvp.Value;
            }
        }

        startInfo.Arguments = arguments; //arguments
        startInfo.CreateNoWindow = true; //don't create a window
        startInfo.RedirectStandardError = true; //redirect standard error
        startInfo.RedirectStandardOutput = true; //redirect standard output
        startInfo.RedirectStandardInput = false;
        startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'

        //startInfo.WindowStyle = ProcessWindowStyle.Normal;
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;
        startInfo.ErrorDialog = false;

        if (exePath.Contains("\\"))
        {
            startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(exePath);
        }

        using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
        {
            //subscribe to event and add event handler code
            p.ErrorDataReceived += (sender, e) =>
            {
                if (!String.IsNullOrEmpty(e.Data))
                {
                    //ToDo: add desired code 
                    //Debug.WriteLine("Error: " + e.Data);
                    OnErrorReceived(errMsg); //raise event
                }
            };

            //subscribe to event and add event handler code
            p.OutputDataReceived += (sender, e) =>
            {
                if (!String.IsNullOrEmpty(e.Data))
                {
                    //ToDo: add desired code
                    //Debug.WriteLine("Output: " + e.Data);
                    OnOutputReceived(e.Data); //raise event
                }
            };

            p.Start(); //start

            p.BeginErrorReadLine(); //begin async reading for standard error
            p.BeginOutputReadLine(); //begin async reading for standard output

            //waits until the process is finished before continuing
            p.WaitForExit();

        }
    }
    catch(System.ComponentModel.Win32Exception ex)
    {
        errMsg = "Error (Win32Exception): " + ex.Message;
        //Debug.WriteLine(errMsg);
        OnErrorReceived(errMsg);
    }
    catch(Exception ex)
    {
        errMsg = "Error: " + ex.Message;
        //Debug.WriteLine(errMsg);
        OnErrorReceived(errMsg);
    }
    
}

Helper.cs(完整代码)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ProcessUsage
{
    public class Helper
    {
        //delegates
        public delegate void EventHandlerProcessErrorReceived(object sender, string data);
        public delegate void EventHandlerProcessOutputReceived(object sender, string data);

        //event that subscribers can subscribe to
        public event EventHandlerProcessErrorReceived ProcessErrorReceived;
        public event EventHandlerProcessOutputReceived ProcessOutputReceived;

        private void OnErrorReceived(string data)
        {
            //only raise event if there are subscribers and actual data exists
            if (ProcessErrorReceived != null && !String.IsNullOrEmpty(data))
            {
                //raise event
                ProcessErrorReceived(this, data);
            }
        }

        private void OnOutputReceived(string data)
        {
            //only raise event if there are subscribers and actual data exists
            if (ProcessOutputReceived != null && !String.IsNullOrEmpty(data))
            {
                //raise event
                ProcessOutputReceived(this, data);
            }
        }

        public void RunCmd(string exePath, string arguments = null, Dictionary<string, string> environmentVarDict = null)
        {
            string errMsg = string.Empty;

            try
            {
                if (String.IsNullOrEmpty(exePath))
                {
                    errMsg = "Error: fullyQualifiedExePath not specified";

                    Debug.WriteLine("Error: " + errMsg);
                    OnErrorReceived(errMsg); //raise event

                    return;
                }

                //create new instance
                ProcessStartInfo startInfo = new ProcessStartInfo(exePath, arguments);

                //add environment variables, if specified
                if (environmentVarDict != null)
                {
                    foreach (KeyValuePair<string, string> kvp in environmentVarDict)
                    {
                        //add environment variable
                        startInfo.EnvironmentVariables[kvp.Key] = kvp.Value;
                    }
                }

                startInfo.Arguments = arguments; //arguments

                startInfo.CreateNoWindow = true; //don't create a window

                startInfo.RedirectStandardError = true; //redirect standard error
                startInfo.RedirectStandardOutput = true; //redirect standard output

                startInfo.RedirectStandardInput = false;

                startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'

                //startInfo.WindowStyle = ProcessWindowStyle.Normal;
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
                startInfo.ErrorDialog = false;

                if (exePath.Contains("\\"))
                {
                    startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(exePath);
                }

                using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
                {
                    //subscribe to event and add event handler code
                    p.ErrorDataReceived += (sender, e) =>
                    {
                        if (!String.IsNullOrEmpty(e.Data))
                        {
                            //ToDo: add desired code 
                            //Debug.WriteLine("Error: " + e.Data);
                            OnErrorReceived(errMsg); //raise event
                        }
                    };

                    //subscribe to event and add event handler code
                    p.OutputDataReceived += (sender, e) =>
                    {
                        if (!String.IsNullOrEmpty(e.Data))
                        {
                            //ToDo: add desired code
                            //Debug.WriteLine("Output: " + e.Data);
                            OnOutputReceived(e.Data); //raise event
                        }
                    };

                    p.Start(); //start

                    p.BeginErrorReadLine(); //begin async reading for standard error
                    p.BeginOutputReadLine(); //begin async reading for standard output

                    //waits until the process is finished before continuing
                    p.WaitForExit();

                }
            }
            catch(System.ComponentModel.Win32Exception ex)
            {
                errMsg = "Error (Win32Exception): " + ex.Message;
                //Debug.WriteLine(errMsg);
                OnErrorReceived(errMsg);
            }
            catch(Exception ex)
            {
                errMsg = "Error: " + ex.Message;
                //Debug.WriteLine(errMsg);
                OnErrorReceived(errMsg);
            }
        }
    }
}

用法

Helper helper = new Helper();

//subscribe to event and add event handler
helper.ProcessErrorReceived += (s, data ) =>
{
    Debug.WriteLine("helper.ProcessErrorReceived: " + data);
};

//subscribe to event and add event handler
helper.ProcessOutputReceived += (s, data) =>
{
    Debug.WriteLine("helper.ProcessOutputReceived: " + data);
};

示例 1

helper.RunCmd("test");

示例 2

helper.RunCmd("ipconfig", "/all");

示例 3

helper.RunCmd(@"C:\Windows\system32\ipconfig.exe", "/all");

示例 4

Dictionary<string, string> environmentVarsDict = new Dictionary<string, string>();
environmentVarsDict.Add("environmentVar1", "myValue1");
environmentVarsDict.Add("environmentVar2", "myValue2");

helper.RunCmd("test123", "/c", environmentVarsDict);

【讨论】:

  • 感谢您考虑周全的答案,但我们已经在管理流程本身,几乎与您的示例代码相同。根据 OP,问题非常具体,即捕获的单个错误响应(由您的示例中的 OnErrorReceived 函数)呈现为两个单独的事件,因为在单个错误消息中嵌入了 CRLF。不幸的是,没有任何回旋余地来使用备用 shell,因此对整个捕获的错误输出进行后处理以删除重复项是我唯一的选择。
  • @mrseank:因为我捕获了System.ComponentModel.Win32Exception,所以您发布的错误 (...is not recognized as an internal or external command, operable program or batch file) 永远不会发生。相反,会出现以下消息:Error (Win32Exception): The system cannot find the file specified - 都在一行
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-07
  • 1970-01-01
  • 1970-01-01
  • 2016-06-20
相关资源
最近更新 更多