根据环境

时间:2017-07-15 20:49:59

标签: powershell

考虑一下powershell命令:

cmd.exe "/c start notepad.exe"

使用powershell.exe(控制台)此命令在启动cmd / notepad进程后立即完成,并且不等待notepad / cmd退出。如果我希望它等待完成,我必须将输出传递给Out-Null。

使用powershell ISE,此命令会阻止执行,直到notepad / cmd关闭。

此外,如果我使用System.Diagnostics.Process在powershell.exe中运行的脚本中创建一个新进程(powershell.exe)并重定向标准输出,则脚本现在会阻止执行,直到关闭notepad / cmd。如果我不重定向输出,它不会阻止执行。

但是如果我使用c#创建一个具有相同设置/开始信息的新进程,并使用重定向输出运行相同的脚本,则不会阻止执行。

我在这里不知所措。显然它与执行和输出的设置有关,也许与“cmd.exe”有关。我希望有人能理解在这些情况下幕后发生的根本不同的事情。我知道ISE和cmd.exe不完全支持,但我不知道为什么其他3不一致。

为什么脚本不能以相同的行为运行(特别是powershell控制台)以及如何使用它们?

编辑:

经过一些故障排除后,我能够让所有的powershell.exe版本运行相同(ISE对我来说并不重要)。 cmd.exe "/c start notepad.exe"阻止执行的奇数球是在powershell中创建新进程以使用System.Diagnostics.Process运行powershell.exe时。如果输出被重定向(我首先使用System.Diagnostics.Process的原因,Start-Process不支持重定向除了文件),那么在进程对象上调用WaitForExit()会导致阻塞发生。

简单地用WaitForExit()替换Wait-Process(和进程ID)会导致在新的powershell.exe中运行的powershell脚本按预期执行,并在运行命令后退出。希望这些信息与@ mklement0答案相结合对于其他有类似问题的人来说已经足够了。

1 个答案:

答案 0 :(得分:1)

要在常规控制台和ISE中获得可预测的行为

  • 异步启动GUI应用程序 (立即返回提示/继续执行脚本):

    • notepad.exe

    • 直接调用记事本使其以异步方式运行,因为它是一个GUI子系统应用程序。

    • 如果您仍希望 跟踪该流程,稍后检查它是否仍在运行,以及终止时退出代码是,请使用{{1} },这使-PassThru返回[System.Diagnostic.Process]个实例:

      • Start-Process

        • $process = Start-Process -PassThru notepad.exe稍后会告诉您该进程是否仍在运行。
        • 退出后,$process.HasExited会告诉您退出代码(对于GUI应用程序,这可能不会告诉您太多)。
      • 要等待同步(在某些时候):

        • 使用$process.ExitCode等待(无限期)终止进程。
        • 以秒为单位添加Wait-Process $process.ID值以限制等待时间;如果流程未在超时期限内终止,则会报告非终止错误,导致-Timeout反映$?
  • 同步启动GUI应用程序 (阻止应用程序终止):

    • $False

    • Start-Process -Wait notepad.exe告诉Start-Process等待创建的进程终止;这相当于-Wait

请注意,对于 控制台 - 子系​​统应用程序(例如cmd /c 'start /wait notepad.exe'), 同步执行是默认< /强>;只有GUI应用程序才需要findstr.exe,包括显式创建新控制台窗口以运行命令。

以异步方式运行控制台应用程序或shell命令 (无需打开新的控制台窗口),您有以下选择:

  • [首选] 使用Start-Job启动命令,Receive-Job稍后接收其输出/成功状态

    • Start-Process
    • 要同步等待此作业完成(并自动将其删除),请使用
      $j = Start-Job { sleep 2; 'hi' }

    • PowerShell 核心 中,您可以使用更简单Receive-Job -Wait -AutoRemoveJob $j语法(如 类似POSIX的Unix shell,例如... &)代替bash; e.g:

      • Start-Job
  • [不可取]您可以使用$j = & { sleep 2; 'hi!' } &之类的内容,但这是不明智的:

    • 通过Start-Process -NoNewWindow powershell -Args ...将shell命令传递给powershell,需要根据晦涩的规则进行错综复杂的引用 - 请参阅我的this answer了解背景信息。
    • 应用程序/ shell命令生成的任何stdout和stderr输出默认情况下会在当前控制台中以异步方式到达,并穿插您正在以交互方式执行的操作。
    • 虽然您可以使用Start-ProcessRedirectStandardOutput(并通过-RedirectStandardError输入stdin)将这些流重定向到文件,然后您可以使用-RedirectStandardInput要获取流程信息对象以确定流程的状态和退出代码,-PassThruStart-Job是一种更简单,更方便的替代方案。

P.S。:我没有解释为什么Receive-Job在ISE中阻塞但在常规控制台中没有阻止。然而,鉴于上述解决方案,可能不需要找到这种差异的底部。