我们在C:testtest.ps1
中有一个Powershell脚本。该脚本包含以下内容(未删除任何内容(:
Start-Process -NoNewWindow powershell { sleep 30; }
sleep 10
当我们打开命令行窗口(cmd.exe
(并通过以下命令执行该脚本时
c:WindowsSystem32WindowsPowerShellv1.0powershell.exe -File C:testtest.ps1
我们可以在Windows任务管理器(以及Sysinternals Process Explorer(中看到,脚本的行为与预期一致:
- 在执行了上述命令之后,进程列表中立即出现两个新条目,其中一个是Powershell执行";主";脚本(
test.ps1
(,其中一个是执行"脚本"的Powershell;背景脚本";({ sleep 30; }
( - 10秒后,第一个条目(与
test.ps1
相关(从进程列表中消失,而第二个条目保留在进程列表中 - 当额外的20秒(即总共30秒(过去时,第二个条目(与
{ sleep 30; }
相关(也会从进程列表中消失
这是预期的行为,因为除非给定-Wait
,否则Start-Process
无论如何都会在后台启动新进程。到目前为止,一切都很好。
但现在我们遇到了一个棘手的问题,这个问题已经花了我们两天的调试时间,直到我们最终找出其中一个脚本行为不端的原因:
实际上,test.ps1
是通过SSH执行的
也就是说,我们已经在Windows server 2019上安装了微软的OpenSSH服务器实现,并对其进行了正确配置。使用其他机器(Linux和Windows(上的SSH客户端,我们可以登录到Windows Server,通过在客户端上执行以下命令,我们可以通过SSH在服务器上执行test.ps1
:
ssh -i <appropriate_key> administrator@ip.of.windows.server c:WindowsSystem32WindowsPowerShellv1.0powershell.exe -File C:testtest.ps1
在观察Windows Server上的任务管理器时,一旦在客户端上执行此命令,我们就可以再次看到进程列表中的两个新条目,如上所述。
但是,这两个条目将在10秒钟后从服务器上的进程列表中消失
这意味着一旦主进程结束,后台进程({ sleep 30; }
(就会被终止。这与记录在案的情况和应该发生的情况相反,我们真的需要防止它
所以问题是:
我们如何更改test.ps1
,使后台进程({ sleep 30; }
(在脚本结束时的任何情况下都不会被杀死,即使脚本是通过SSH启动的?
一些旁注:
这不是一个学术性的例子。实际上,我们在服务器上有一个相当复杂的脚本系统,它由大约十几个Powershell脚本组成,其中一个是";主";如上所示,在后台执行其他脚本的脚本;背景脚本本身反过来可能启动进一步的背景脚本。
重要的是首先不要通过SSH在后台启动主脚本。客户端必须处理主脚本的输出和退出代码,并且必须等待主脚本完成一些工作并返回。
这意味着我们必须使用Powershell的功能来启动主脚本中的后台进程(或者启动能够启动后台进程的第三方程序,这些后台进程在主脚本结束时不会被杀死(。
注意:在此处使用作业的原始建议是不正确的,因为当作业与父会话一起停止时,任何子进程仍然会被终止。由于这是对这种情况的错误建议,我已经从这个答案中删除了该内容。
不幸的是,当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
命令继续运行,直到我自己在远程服务器上停止了进程。
这里的缺点是,您似乎没有使用PowerShell远程处理,而是通过SSH调用shell命令,而SSH恰好是PowerShell脚本。如果需要PowerShell远程处理的SSH传输,则还必须使用PowerShell Core。