【问题标题】:What exactly does the call operator in PowerShell do?PowerShell 中的调用运算符到底是做什么的?
【发布时间】:2014-04-29 21:32:42
【问题描述】:

我有一个 powershell 脚本,它使用调用运算符运行一些可执行文件。像这样的:

$executable = 'c:\dev\test.exe'
& $executable | Out-Host
Write-Host 'done'

test.exe 反过来运行另一个程序 (server.exe) 并通过 TCP/IP 与其通信(该程序需要管理权限,因此我的 powershell 脚本以管理员身份运行),它也运行简单的批处理脚本(运行并在 1 秒内终止)。一旦启动,server.exe 将一直运行,直到被 test.exe 终止。所有涉及的程序都是控制台应用程序。另一个可能值得注意的事实是,由于server.exe 需要管理员权限,因此它在与test.exe 不同的控制台窗口中运行。

有时test.exe 不会终止server.exe(有时是由于崩溃,有时是由于其中的错误)。发生这种情况时,test.exe 终止执行后不会返回到 powershell 脚本(我没有看到打印“完成”)。如果我手动终止 server.exe,我会立即在 powershell 控制台中看到“完成”。为什么会这样?为什么powershell等待进程终止它没有启动,甚至没有在同一个控制台中运行?

在我的调查尝试中,我尝试用更少的代码重现问题,我创建了 test2.exe,它只是运行 server.exe(完全相同,我可以访问 test.exe 源)并终止。现在,当我运行相同的 powershell 脚本但使用 test2.exe$executable 时,我看到的正是我最初期望看到的:server.exe 在单独的窗口中启动,然后在 powershell 窗口中打印“完成”(并且 powershell 脚本终止) 而server.exe 仍在运行。

我在 test.exe 运行时观看了 Process Explorer,我确信在 test.exe 终止后除了 server.exe 之外没有其他进程正在运行。

我没有找到任何深入的解释 & 运算符做什么以及它在等待什么,也许你有一些想法?

更新:我尝试了Start-Process,而不是使用呼叫操作员运行:

$executable = 'c:\dev\test.exe'
Start-Process $executable -Wait
Write-Host 'done'

现在test.exe 在它自己的控制台中运行,终止并且powershell 仍在等待server.exe 完成。

我也尝试运行批处理脚本:

c:\dev\test.exe
echo done

它工作正常:只要test.exe 终止,“完成”就会打印到控制台并且批处理文件停止(server.exe 仍在运行)。

另一个更新:更改脚本以执行以下操作:

$executable = 'c:\dev\test.exe'
$proc = Start-Process $executable -PassThru
do {
   Get-Process -Id $proc.Id
   Start-Sleep -Seconds 10
} while ($true)

test.exe 正在运行时,我得到了几次常规的Get-Process 输出然后(在test.exe 终止之后)它开始产生“找不到进程标识符为3028 的进程”错误。所以我大概可以走这条路。

我尝试的另一件事是:

$env:PATH += ';c:\dev\;'
test.exe
Write-Host 'done'

仍然没有运气:test.exe 终止,powershell 仍在等待 server.exe 终止。

也尝试了工作方法:

$job = Start-Job {& 'c:\dev\test.exe'}
do
{
    $job
    Start-Sleep -Seconds 10
} while ($job.JobStateInfo.State -eq [System.Management.Automation.JobState]::Running)

Receive-Job $job

除非我终止server.exe,否则它永远不会退出。

【问题讨论】:

  • Keith Hill 之类的人对您的回复可能让我不知所措,但是您是否考虑过将可执行文件作为一项工作来运行?这样,即使它没有让 PS 知道它由于某种原因已终止,您也应该能够运行 get-process 并检查它是否正在运行,然后采取相应的行动。
  • @TheMadTechnician,我如何将可执行文件作为作业运行? Start-Job据我所知只能运行powershell脚本。
  • 是的,作业是针对脚本的,但这只是意味着您在命令周围加上花括号并将其视为脚本块。 $myjob = Start-job {& test.exe}

标签: powershell process console-application


【解决方案1】:

调用运算符& 只是尝试调用紧随其后的命名命令(应用程序、脚本、函数、cmdlet),并将任何其他参数传递给命令。此外,& 在新范围内执行命令,而. 在当前范围内执行命令。执行应用程序(exe)时,我认为您在新范围内执行并不重要。 exe 不能影响 PowerShell 中的作用域变量。

WRT test.exe 行为,在控制台 exe 完成之前,PowerShell 不会将控制权返回给脚本。 Test.exe 和 test2.exe 都是控制台子系统 exe 对吗? PowerShell 与 Windows 子系统 exe 的行为不同。除非您使用 notepad.exe | Out-Null 这样的技巧,否则它会启动它们并立即返回。

【讨论】:

  • 是的,test.exe 和 test2.exe 都是控制台子系统可执行文件。我验证了 test.exe 终止,但由于某种原因 powershell 等待 server.exe 终止。
  • 它如何调用可执行文件? CreateProcessShellExecute 或别的什么?产卵过程后它究竟在等待什么?
  • 通过检查代码,如果扩展是可执行文件,它看起来会使用 System.Diagnostics.Process.Start()。所以在那种情况下,我很确定它没有使用ShellExecute。此外,对于控制台应用程序,它不只是启动应用程序。 PowerShell 捕获应用程序 stdout/stderr IO。这允许应用程序将文本输出到 PowerShell 控制台。结果是 PowerShell 会等到控制台应用程序退出后再继续执行下一个命令。
猜你喜欢
  • 2016-02-14
  • 1970-01-01
  • 2019-06-10
  • 2015-03-28
  • 2021-07-15
  • 1970-01-01
  • 2014-09-16
  • 2013-08-08
  • 1970-01-01
相关资源
最近更新 更多