如何防止 Powershell 在通过 SSH 生成时杀死自己的后台进程?

How to prevent Powershell from killing its own background processes when spawned via SSH?

我们在 C:\test\test.ps1 中有一个 Powershell 脚本。该脚本具有以下内容(未删除任何内容):

Start-Process -NoNewWindow powershell { sleep 30; }
sleep 10

当我们打开命令行 window (cmd.exe) 并通过以下命令执行该脚本时

c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File C:\test\test.ps1

我们可以在 Windows 任务管理器(以及 Sysinternals Process Explorer)中看到脚本的行为符合预期:

这是预期的行为,因为 Start-Process 无论如何都会在后台启动新进程,除非给出 -Wait。到目前为止,还不错。

但是现在我们遇到了一个毛茸茸的问题,它已经花费了我们两天的调试时间,直到我们最终找出其中一个脚本行为不当的原因:

其实test.ps1是通过SSH执行的

也就是说,我们已经在 Windows Server 2019 上安装了 Microsoft 的 OpenSSH 服务器实现,并已正确配置。在其他机器(Linux和Windows)上使用SSH客户端,我们可以登录到Windows服务器,我们可以通过执行以下命令在服务器上通过SSH执行test.ps1客户端命令:

ssh -i <appropriate_key> administrator@ip.of.windows.server c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File C:\test\test.ps1

观察Windows服务器上的任务管理器,我们可以再次看到进程列表中的两个新条目,如上所述,只要在客户端上执行此命令。

但是,两个 条目在 10 秒后从服务器上的进程列表中消失。

这意味着主进程一结束,后台进程 ({ sleep 30; }) 就会被杀死。这与记录的内容和应该发生的事情相反,我们确实需要防止它。

所以问题是:

我们如何更改 test.ps1 以便在脚本结束时后台进程 ({ sleep 30; }) 在任何情况下都不会被终止,即使脚本是通过 SSH 启动的?

一些旁注:

Note: The original advice to use jobs here was incorrect, as when the job is stopped along with the parent session, any child processes still get killed. As it was incorrect advice for this scenario, I've removed that content from this answer.

不幸的是,当 PowerShell 会话结束时,从该会话创建的子进程也会结束。但是,当使用 PSRemoting 时,我们可以使用 -InDisconnectedSession 参数(别名为 -Disconnected)告诉 Invoke-Command 到 运行 断开连接的会话中的命令:

$icArgs = @{
  ComputerName = 'RemoteComputerName'
  Credential = ( Get-Credential )
  Disconnected = $true
}

Invoke-Command @icArcs { ping -t 127.0.0.1 }

根据我对无限 ping 的测试,如果父 PowerShell 会话关闭,远程会话应该继续执行。在我的例子中,ping 命令持续 运行ning 直到我自己在远程服务器上停止了进程。

这里的缺点是您似乎没有使用 PowerShell Remoting,而是通过 SSH 调用 shell 命令,而这些命令恰好是 PowerShell 脚本。如果您需要用于 PowerShell 远程处理的 SSH 传输,您还必须使用 PowerShell Core。