运行命令提示符命令

766

有没有办法从C#应用程序内部运行命令提示符命令?如果可以,我该如何执行以下操作:

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

这基本上是将RAR文件嵌入到JPG图像中。我只是想知道是否有一种在C#中自动完成此操作的方式。


7
重复的问题已经在https://dev59.com/LnVC5IYBdhLWcg3wykej上有答案可以实现你想要的功能。 - Matt Hamilton
1
https://dev59.com/om435IYBdhLWcg3whQc5#5367686 有一个更好的答案。 - CAD bloke
17个回答

1150

这就是你需要做的,从C#中运行shell命令。

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

编辑:

这是为了隐藏命令行窗口。

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 执行指定的命令字符串,然后终止


192
执行指定字符串命令,然后终止。 - Scott Ferguson
18
只需告诉命令提示符运行并终止(不等待任何用户输入即可关闭窗口)。 - RameshVel
4
谢谢,还有一个问题。在此过程中是否有方法可以隐藏命令提示符窗口? - user
11
我不明白为什么只有我认为这是个可怕的主意。是的,这可以工作,但完全错误。即使它可以工作,通过生成CMD进程来进行简单的IO操作也是错误的。请阅读System.IO命名空间上的文档。那里有足够的功能来完成你需要做的事情,而无需生成不必要的进程。 - Instance Hunter
67
使用 process.WaitForExit() 等待进程完成后再继续,并使用 process.ExitCode 获取进程的退出代码。 - shindigo
显示剩余27条评论

183

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

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

2
我本来没有抱太大希望,因为我认为我的电脑上可能有一些管理员或反病毒限制,但是上面的代码确实有效!谢谢Ogglas。 - Pete Kozak
9
这行代码:cmd.StartInfo.CreateNoWindow = true; 救了我的一天。 - Ganesh Kamath - 'Code Frenzy'
4
有没有一种方法可以在单个 cmd.StandardInput.WriteLine(@"cd C:\Test; pwd") 中执行多个命令? - Zach Smith
1
如果cmd进程的输出大于标准输出缓冲区(我记得在32位Windows 2000上它是4KB,但最近没有测试过),则会发生死锁。 WaitForExit将等待读取StandardOutput内容(因为缓冲区已满)。最好在另一个线程中读取输出,然后调用WaitForExit。 - robbie fan
如何做到这一点?@robbiefan - Nitin Sawant
创建一个新线程(确保它已启动)以从命令的stdout中读取(将结果存储在某个地方)。然后使用当前线程写入命令的stdin。等待读取线程完成,然后获取结果。@NitinS - robbie fan

50
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);

4
在C#中,@符号是一个特殊字符,用于指示后面的字符串应该被处理为字面量,而不需要转义其中包含的特殊字符。 - Pacerier
12
@Pacerier 这告诉编译器转义所有在字符串中通常需要转义的字符,比如说 \。因此,如果没有这个 \,你的代码看起来会像这样:proc1.FileName = "C:\\Windows\\System32\\cmd.exe"; - James Ko
3
需要注意的是 proc1.Verb = "runas"; 会使该进程以提升的权限运行... 这并不总是有意为之。 - Dinei
1
如何使得这个CMD窗口在运行结束后不关闭? - Hrvoje T
1
我发现在命令行中使用'cd path'与其他命令用'&&'连接时,即使它先出现在命令行中,它也总是最后执行。你的代码:'proc1.WorkingDirectory = @"C:\Windows\System32";'非常有帮助!谢谢! - Nozim Turakulov
显示剩余2条评论

19

由于某些原因,上面的答案都没有帮助,似乎他们会掩盖错误并使命令的故障排除变得困难。所以我最终采用了这样的方法,也许可以帮助其他人:

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。 - script'n'code
1
@copyitright 命名空间和类。如果您只是创建一个新项目,它们将为您生成。 - coinbird
啊,没事了。如果你使用 cmd.exe 应用程序,你可以将命令作为参数传递。 - Zach Smith
为了记录:我想让进程运行 echo Hello World! 并在弹出的命令窗口中显示命令输出。所以我尝试了以下代码:Filename = @"echo"Arguments = "Hello World!"UseShellExecute = falseRedirectStandardOuput = falseCreateNoWindow = false。这允许父应用程序的命令窗口显示 "Hello World!"(这是有意义的,因为标准输出未重定向到子进程)。 - Minh Tran

12

虽然严格意义上这并没有直接回答问题,但它确实回答了原帖想要做的事情:合并文件。如果说有什么作用的话,那就是为新手解释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);
            }
        }
    }
}

11

如果您希望保持cmd窗口打开或者想要在WinForm/WPF中使用它,那么请按如下方式使用。

    string strCmdText;
//For Testing
    strCmdText= "/K ipconfig";

 System.Diagnostics.Process.Start("CMD.exe",strCmdText);

/K

将保持命令窗口打开。


2
不错。找到了这个关于 cmd 命令及其参数的文档 - pius

11

如果你想以异步模式运行命令并打印结果,你可以使用这个类:

    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
这也可以通过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.

不错的选择。 - Alexandru Dicu

8

引用了 Microsoft.VisualBasic 库。

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

8
是的,有一种方法可以实现(请参见Matt Hamilton评论中的链接),但更好的方法是使用.NET的IO类。您可以使用File.ReadAllBytes读取文件,然后使用File.WriteAllBytes写入“嵌入式”版本。

11
将整个文件加载到内存中,只为了将其附加到另一个文件中,并不是非常高效,特别是当文件足够大时。 - Konstantin Spirin
7
尝试理解回答的精神。重点是.NET拥有足够多的IO类和函数来完成此操作,而无需调用操作系统Shell。我提到的特定函数可能不是最好的,但那些只是最简单的。完全没有必要调用Shell来完成这个任务。 - Instance Hunter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接