cmd - 如何从 cmd.exe 重定向标准输出
问题描述
事实证明,这很难搜索,因为大多数结果都是关于从 WITHIN cmd.exe 重定向而不是 cmd.exe 本身的输出。
我有一个简单的 C# 示例,显示了重定向进程输出并仅打印输出值的工作和非工作测试。
void Main()
{
// Calling nslookup directly works as expected
ProcessStartInfo joy = new ProcessStartInfo("nslookup", @"google.com 8.8.8.8");
// Calling nslookup as a command to cmd.exe does not work as expected
ProcessStartInfo noJoy = new ProcessStartInfo(Environment.ExpandEnvironmentVariables("%COMSPEC%"), @"/C nslookup google.com 8.8.8.8");
Console.WriteLine($"*** Running \"{joy.FileName} {joy.Arguments}\"...");
Console.WriteLine();
Run(joy);
Console.WriteLine();
Console.WriteLine($"*** Running \"{noJoy.FileName} {noJoy.Arguments}\"...");
Console.WriteLine();
Run(noJoy);
}
void Run(ProcessStartInfo startInfo)
{
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
Process proc = new Process();
proc.StartInfo = startInfo;
proc.EnableRaisingEvents = true;
proc.Exited += ReceiveExitNotification;
proc.ErrorDataReceived += ReceiveStandardErrorData;
proc.OutputDataReceived += ReceiveStandardOutputData;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
proc.ExitCode.Dump();
}
void ReceiveStandardOutputData(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
void ReceiveStandardErrorData(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
void ReceiveExitNotification(object sender, EventArgs e)
{
Console.WriteLine("Exited");
}
这是我从上面得到的输出
*** 运行“nslookup google.com 8.8.8.8”... 非权威答案: 服务器:dns.google 地址:8.8.8.8 名称:google.com 地址:2607:f8b0:4002:c08::8b 2607:f8b0:4002:c08::64 2607:f8b0:4002:c08::65 2607:f8b0:4002:c08::66 172.217.10.206 无效的 无效的 退出 0 *** 运行“C:\windows\system32\cmd.exe /C nslookup google.com 8.8.8.8”... 无效的 无效的 退出 0
示例中 nslookup 的选择是任意的,我尝试了许多其他命令,包括具有副作用的命令,因此我可以确定它按预期执行。
我尝试过同步读取,但没有改变。
我没有理由相信它与 C# 或 .NET 相关。我可以尝试直接 CreateProcess() 测试来确认。
对于上下文,它是一个批处理文件,我实际上希望从中获取输出,这就是需要中间 cmd.exe 进程的原因。
进一步的上下文,它实际上是一个 MSBuild Exec 任务,我试图从中获取输出,所以我对实际调用的控制有限,但我已经看到任务在调试器中运行并将其缩小到这个问题。
解决方案
TLDR;问题中的代码示例在任何普通机器上都可以正常工作。
所以事实证明这是一个权限问题。这是一台公司计算机,我的权限受到限制,但是他们安装了软件,可以授予特定进程的管理权限。cmd.exe 是这些进程之一,因此默认情况下它以管理员身份启动,因此我无法从非提升进程中读取输出流。
一些几乎可以解决该问题的想法:
从 cmd.exe 提示我可以运行
set __COMPAT_LAYER=RUNASINVOKER
然后运行第二个 cmd.exe,它运行不高,但这并没有真正帮助,因为我仍然无法获得该流。设置 __COMPAT_LAYER 环境变量似乎只影响从 cmd.exe 启动的进程(不是来自 .NET 的 Process.Start() 使用的 CreateProcess() )。RunAs.exe 有一个 /trustlevel 开关,我可以使用它运行未提升的命令,但是我的 Process 对象用于 runas,它不处理任何重定向,甚至在子进程的生命周期内保持打开状态,所以仍然没有好处。
但就我而言,我认为最简单的解决方案是最好的。将 cmd.exe 复制到另一个目录并将其添加到路径的顶部。这修复了提升问题,甚至可以作为我的实际问题的最终解决方案,通过我通过 MSBuild 任务对调用调用的有限访问来处理事件。
推荐阅读
- google-cloud-platform - 为什么 Google Compute Engine 没有运行我的容器?
- android - TcpClient.ConnectAsync 和 TcpClient.BeginConnect 在 Xamarin.Forms Android 应用程序中始终返回 true
- lua - ReplicatedFirst 中的 RemoteEvents 不会触发 OnClientEvent 处理程序
- pandas - Pandas:在 for 循环中创建空数据框,追加
- react-native - React-native-splashscreen 和导航
- google-cloud-platform - 如何从 Google API 和凭据页面访问对象
- websocket - Websocket 连接 - 客户端尝试对一个地址而不是另一个地址进行 TLS 握手
- autoscaling - 如果 Spot 实例不适用于 AWS 自动扩展组,会发生什么情况?
- javascript - 在 Google Analytics(分析)中看到自定义跟踪代码管理器键?
- html - Thymeleaf Content-Type 在 HTML 电子邮件中被忽略(未呈现为 HTML)