【问题标题】:Run Command Prompt Commands运行命令提示符命令
【发布时间】:2020-12-24 17:44:30
【问题描述】:

有没有办法从 C# 应用程序中运行命令提示符命令?如果是这样,我将如何执行以下操作:

copy /b Image1.jpg + Archive.rar Image2.jpg

这基本上是在 JPG 图像中嵌入一个 RAR 文件。我只是想知道是否有办法在 C# 中自动执行此操作。

【问题讨论】:

标签: c# .net command-line command prompt


【解决方案1】:

这就是您从 C# 运行 shell 命令所要做的全部工作

string strCmdText;
strCmdText= "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
System.Diagnostics.Process.Start("CMD.exe",strCmdText);

编辑:

这是隐藏cmd窗口。

System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
process.StartInfo = startInfo;
process.Start();

编辑:2

重要的是参数以/C 开头,否则它将不起作用。 Scott Ferguson怎么说:它“执行字符串指定的命令,然后终止。”

【讨论】:

  • /C 执行字符串指定的命令然后终止
  • 它只是告诉 cmd 运行和终止(不要等待任何用户输入关闭窗口)
  • 谢谢,还有一个问题。在此期间有没有办法隐藏命令提示符?
  • 我不明白为什么我是唯一一个认为这是一个可怕的想法的人。是的,这会起作用,但这是完全错误的。生成 CMD 进程来执行简单的 IO 操作是错误的,即使它有效。通读 System.IO 命名空间的文档。那里有足够多的功能来做你需要做的事情,而不会产生不需要的进程。
  • 仅供参考:使用 process.WaitForExit() 等待进程完成后再继续,使用 process.ExitCode 获取进程的退出代码。
【解决方案2】:

试过RameshVel's solution,但我无法在我的控制台应用程序中传递参数。如果有人遇到同样的问题,这里有一个解决方案:

using System.Diagnostics;

Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();

cmd.StandardInput.WriteLine("echo Oscar");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
Console.WriteLine(cmd.StandardOutput.ReadToEnd());

【讨论】:

  • 好吧,我没有机会认为我的机器上有一些管理员或防病毒限制,但是..上面的代码有效!谢谢奥格拉斯
  • 这一行:cmd.StartInfo.CreateNoWindow = true;拯救了我的一天。
  • 有没有办法在单个cmd.StandardInput.WriteLine(@"cd C:\Test; pwd")中执行多个命令
  • 如果 cmd 进程的输出大于标准输出的缓冲区,就会死锁(我记得在 32 位 Windows 2000 上是 4KB,但最近没有测试过)。 WaitForExit 将等待读取 StandardOutput 内容(因为缓冲区已满)。最好在另一个线程中读取输出,然后调用 WaitForExit。
【解决方案3】:
var proc1 = new ProcessStartInfo();
string anyCommand; 
proc1.UseShellExecute = true;

proc1.WorkingDirectory = @"C:\Windows\System32";

proc1.FileName = @"C:\Windows\System32\cmd.exe";
proc1.Verb = "runas";
proc1.Arguments = "/c "+anyCommand;
proc1.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(proc1);

【讨论】:

  • C# 中的@ 是什么?
  • @Pacerier 它告诉编译器转义字符串中通常必须转义的所有字符,在本例中为 \。因此,如果没有 \,您的代码将类似于 proc1.FileName = "C:\\Windows\\System32\\cmd.exe";
  • 应该注意proc1.Verb = "runas"; 使这个进程以提升的权限运行......这并不总是有意的。
  • 如何让这个cmd窗口在完成后不关闭?
  • 我发现调用由 '&&' 加入的 'cd path' 与该行中的其他命令总是最后执行,即使它先执行。你的:'proc1.WorkingDirectory = @"C:\Windows\System32";'很有帮助!谢谢!
【解决方案4】:

出于某种原因,上述答案均无济于事,似乎它们将错误扫到了地毯下,并使故障排除一个人的命令变得困难。所以我最终选择了这样的东西,也许它会帮助别人:

var proc = new Process
{
    StartInfo = new ProcessStartInfo
    {
        FileName = @"C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\tf.exe",
        Arguments = "checkout AndroidManifest.xml",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true,
        WorkingDirectory = @"C:\MyAndroidApp\"
    }
};

proc.Start();

【讨论】:

  • 如果我想将它编译成一个独立的控制台应用程序,还需要添加哪些其他代码才能使其工作? (我是所有这些编程的菜鸟,只做了一些脚本)。我正在使用 csc.exe 顺便说一句。
  • @copyitright 命名空间和类。如果您只是创建一个新项目,它们将为您生成。
  • 啊。没关系。如果您使用cmd.exe 应用程序,您可以将命令作为参数传递。
  • 为了后代:我希望进程运行echo Hello World!并在弹出的cmd窗口中显示命令输出。所以我尝试了:Filename = @"echo"Arguments = "Hello World!"UseShellExecute = falseRedirectStandardOuput = falseCreateNoWindow = false。这允许父应用程序的 cmd 窗口显示“Hello World!” (这是有道理的,因为 stdout 没有被重定向到子进程)。
【解决方案5】:

虽然从技术上讲,这并不能直接回答提出的问题,但它确实回答了如何做原始发帖人想做的事情:合并文件。如果有的话,这篇文章是为了帮助新手了解 Instance Hunter 和 Konstantin 在谈论什么。

这是我用来合并文件的方法(在本例中为 jpg 和 zip)。请注意,我创建了一个缓冲区,其中填充了 zip 文件的内容(以小块而不是一个大的读取操作),然后缓冲区被写入 jpg 文件的后面,直到 zip 文件的结尾是到达:

private void CombineFiles(string jpgFileName, string zipFileName)
{
    using (Stream original = new FileStream(jpgFileName, FileMode.Append))
    {
        using (Stream extra = new FileStream(zipFileName, FileMode.Open, FileAccess.Read))
        {
            var buffer = new byte[32 * 1024];

            int blockSize;
            while ((blockSize = extra.Read(buffer, 0, buffer.Length)) > 0)
            {
                original.Write(buffer, 0, blockSize);
            }
        }
    }
}

【讨论】:

    【解决方案6】:

    您可以在一行中使用CliWrap

    var result = await Cli.Wrap("cmd")
        .WithArguments("copy /b Image1.jpg + Archive.rar Image2.jpg")
        .ExecuteBufferedAsync();
    
    var stdOut = result.StandardOutput;
    

    【讨论】:

    • 我对此表示赞成...但现在似乎缺少回购:Unable to find package 'CliWrap' at source
    • @ZachSmith 不确定您的意思,nuget.org/packages/CliWrap 似乎工作正常。原来的链接也是。
    • Ah.sorry.. 由于某种原因,当我无法通过 vpn 连接到我的 nuget 存储库时,我无法安装此软件包。 nuget 对我来说仍然是一个谜。一定是设置错了
    【解决方案7】:

    如果你想保持 cmd 窗口打开或者想在 winform/wpf 中使用它,那么像这样使用它

        string strCmdText;
    //For Testing
        strCmdText= "/K ipconfig";
    
     System.Diagnostics.Process.Start("CMD.exe",strCmdText);
    

    /K

    将保持 cmd 窗口打开

    【讨论】:

    【解决方案8】:

    如果您想在异步模式下运行命令 - 并打印结果。你可以上这堂课吗:

        public static class ExecuteCmd
    {
        /// <summary>
        /// Executes a shell command synchronously.
        /// </summary>
        /// <param name="command">string command</param>
        /// <returns>string, as output of the command.</returns>
        public static void ExecuteCommandSync(object command)
        {
            try
            {
                // create the ProcessStartInfo using "cmd" as the program to be run, and "/c " as the parameters.
                // Incidentally, /c tells cmd that we want it to execute the command that follows, and then exit.
                System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
                // The following commands are needed to redirect the standard output. 
                //This means that it will be redirected to the Process.StandardOutput StreamReader.
                procStartInfo.RedirectStandardOutput =  true;
                procStartInfo.UseShellExecute = false;
                // Do not create the black window.
                procStartInfo.CreateNoWindow = true;
                // Now we create a process, assign its ProcessStartInfo and start it
                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo = procStartInfo;
                proc.Start();
    
                // Get the output into a string
                string result = proc.StandardOutput.ReadToEnd();
    
                // Display the command output.
                Console.WriteLine(result);
            }
            catch (Exception objException)
            {
                // Log the exception
                Console.WriteLine("ExecuteCommandSync failed" + objException.Message);
            }
        }
    
        /// <summary>
        /// Execute the command Asynchronously.
        /// </summary>
        /// <param name="command">string command.</param>
        public static void ExecuteCommandAsync(string command)
        {
            try
            {
                //Asynchronously start the Thread to process the Execute command request.
                Thread objThread = new Thread(new ParameterizedThreadStart(ExecuteCommandSync));
                //Make the thread as background thread.
                objThread.IsBackground = true;
                //Set the Priority of the thread.
                objThread.Priority = ThreadPriority.AboveNormal;
                //Start the thread.
                objThread.Start(command);
            }
            catch (ThreadStartException )
            {
                // Log the exception
            }
            catch (ThreadAbortException )
            {
                // Log the exception
            }
            catch (Exception )
            {
                // Log the exception
            }
        }
    
    }
    

    【讨论】:

      【解决方案9】:

      是的,有(参见 Matt Hamilton 评论中的链接),但使用 .NET 的 IO 类会更容易更好。您可以使用 File.ReadAllBytes 读取文件,然后使用 File.WriteAllBytes 编写“嵌入式”版本。

      【讨论】:

      • 将整个文件加载到内存中只是为了将一个附加到另一个不是很有效,尤其是在文件足够大的情况下。
      • 试着看看答案的精神。关键是 .NET 有足够多的 IO 类和函数来执行此操作,而无需调用 OS shell。我提到的特定功能可能不是最好的,但那些只是最简单的。调用 shell 来执行此操作完全没有任何意义。
      【解决方案10】:

      参考Microsoft.VisualBasic

      Interaction.Shell("copy /b Image1.jpg + Archive.rar Image2.jpg", AppWinStyle.Hide);
      

      【讨论】:

        【解决方案11】:

        这也可以通过P/Invoking C 标准库的system 函数来完成。

        using System.Runtime.InteropServices;
        
        [DllImport("msvcrt.dll")]
        public static extern int system(string format);
        
        system("copy Test.txt Test2.txt");
        

        输出:

              1 file(s) copied.
        

        【讨论】:

        • 不错的选择。
        【解决方案12】:

        这里有一点简单和更少代码的版本。它也会隐藏控制台窗口-

        System.Diagnostics.Process process = new System.Diagnostics.Process();
        process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        process.StartInfo.FileName = "cmd.exe";
        process.StartInfo.Arguments = "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
        process.Start();
        

        【讨论】:

          【解决方案13】:

          您可以使用以下方法来实现这一点(如其他答案中所述):

          strCmdText = "'/C some command";
          Process.Start("CMD.exe", strCmdText);
          

          当我尝试上面列出的方法时,我发现我的自定义命令无法使用上面某些答案的语法。

          我发现更复杂的命令需要用引号封装起来才能工作:

          string strCmdText;
          strCmdText = "'/C cd " + path + " && composer update && composer install -o'";
          Process.Start("CMD.exe", strCmdText);
          

          【讨论】:

          • 根据我的经验,当文件路径包含空格时,解析器会被该空格分割。
          【解决方案14】:

          我有以下方法,用于从 C# 运行命令提示符命令

          在第一个参数中传递你要运行的命令

          public static string RunCommand(string arguments, bool readOutput)
          {
              var output = string.Empty;
              try
              {
                  var startInfo = new ProcessStartInfo
                  {
                      Verb = "runas",
                      FileName = "cmd.exe",
                      Arguments = "/C "+arguments,
                      WindowStyle = ProcessWindowStyle.Hidden,
                      UseShellExecute = false,
                      CreateNoWindow = true,
                      RedirectStandardOutput = true,
                      RedirectStandardError = false
                  };
          
                  var proc = Process.Start(startInfo);
          
                  if (readOutput)
                  {
                      output = proc.StandardOutput.ReadToEnd(); 
                  }
          
                  proc.WaitForExit(60000);
          
                  return output;
              }
              catch (Exception)
              {
                  return output;
              }
          }
          

          【讨论】:

            【解决方案15】:

            您可以简单地使用.bat格式扩展名编写代码,批处理文件的代码:

            c:/ copy /b Image1.jpg + Archive.rar Image2.jpg

            使用这个 c# 代码:

            Process.Start("file_name.bat")

            【讨论】:

            • 如果您想在运行时隐藏 cmd,您可以使用 .vbs 格式扩展的简单 Visual Basic 脚本代码,代码:CreateObject("Wscript.Shell").Run "filename.bat",0,True
            【解决方案16】:

            这可能有点读,所以我很抱歉提前。这是我尝试和测试的方法,可能有更简单的方法,但这是我把代码扔到墙上,看看卡住了什么

            如果可以使用批处理文件完成,那么可能过于复杂的解决方法是让 c# 编写一个 .bat 文件并运行它。如果您想要用户输入,您可以将输入放入变量中并让 c# 将其写入文件。这种方式需要反复试验,因为它就像用另一个木偶控制一个木偶。

            这里是一个例子,在这种情况下,该功能是用于清除打印队列的 Windows 论坛应用程序中的一个按钮。

            using System.IO;
            using System;
            
               public static void ClearPrintQueue()
                {
            
                    //this is the path the document or in our case batch file will be placed
                    string docPath =
                     Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                    //this is the path process.start usues
                    string path1 = docPath + "\\Test.bat";
            
                    // these are the batch commands
                    // remember its "", the comma separates the lines
                    string[] lines =
                    {
                        "@echo off",
                        "net stop spooler",
                        "del %systemroot%\\System32\\spool\\Printers\\* /Q",
                        "net start spooler",
                        //this deletes the file
                        "del \"%~f0\"" //do not put a comma on the last line
                    };
            
                    //this writes the string to the file
                    using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
                    {
                        //This writes the file line by line
                        foreach (string line in lines)
                            outputFile.WriteLine(line);
                    }
                    System.Diagnostics.Process.Start(path1);
            
                }
            

            如果您想要用户输入,那么您可以尝试这样的操作。

            这用于将计算机 IP 设置为静态但询问用户 IP、网关和 dns 服务器是什么。

            您需要this 才能使用它

            public static void SetIPStatic()
                {
            //These open pop up boxes which ask for user input
                    string STATIC = Microsoft.VisualBasic.Interaction.InputBox("Whats the static IP?", "", "", 100, 100);
                    string SUBNET = Microsoft.VisualBasic.Interaction.InputBox("Whats the Subnet?(Press enter for default)", "255.255.255.0", "", 100, 100);
                    string DEFAULTGATEWAY = Microsoft.VisualBasic.Interaction.InputBox("Whats the Default gateway?", "", "", 100, 100);
                    string DNS = Microsoft.VisualBasic.Interaction.InputBox("Whats the DNS server IP?(Input required, 8.8.4.4 has already been set as secondary)", "", "", 100, 100);
            
            
            
                    //this is the path the document or in our case batch file will be placed
                    string docPath =
                     Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                    //this is the path process.start usues
                    string path1 = docPath + "\\Test.bat";
            
                    // these are the batch commands
                    // remember its "", the comma separates the lines
                    string[] lines =
                    {
                        "SETLOCAL EnableDelayedExpansion",
                        "SET adapterName=",
                        "FOR /F \"tokens=* delims=:\" %%a IN ('IPCONFIG ^| FIND /I \"ETHERNET ADAPTER\"') DO (",
                        "SET adapterName=%%a",
                        "REM Removes \"Ethernet adapter\" from the front of the adapter name",
                        "SET adapterName=!adapterName:~17!",
                        "REM Removes the colon from the end of the adapter name",
                        "SET adapterName=!adapterName:~0,-1!",
            //the variables that were set before are used here
                        "netsh interface ipv4 set address name=\"!adapterName!\" static " + STATIC + " " + STATIC + " " + DEFAULTGATEWAY,
                        "netsh interface ipv4 set dns name=\"!adapterName!\" static " + DNS + " primary",
                        "netsh interface ipv4 add dns name=\"!adapterName!\" 8.8.4.4 index=2",
                        ")",
                        "ipconfig /flushdns",
                        "ipconfig /registerdns",
                        ":EOF",
                        "DEL \"%~f0\"",
                        ""
                    };
            
                    //this writes the string to the file
                    using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
                    {
                        //This writes the file line by line
                        foreach (string line in lines)
                            outputFile.WriteLine(line);
                    }
                    System.Diagnostics.Process.Start(path1);
            
                }
            

            就像我说的。它可能有点过于复杂,但除非我将批处理命令写错,否则它永远不会失败。

            【讨论】:

              【解决方案17】:

              您可以使用RunProcessAsTask pacakge 并像这样轻松地异步运行您的进程:

              var processResults = await ProcessEx.RunAsync("git.exe", "pull");
              //get process result
              foreach (var output in processResults.StandardOutput)
              {
                 Console.WriteLine("Output line: " + output);
              }
              

              【讨论】: