为何点源调用含Start-Process的PowerShell脚本未按预期返回?
原问题
我有一个PowerShell脚本c:\startnotepad.ps1,内容仅一行:
Start-Process c:\windows\notepad.exe
该脚本未使用-Wait参数,理论上启动notepad.exe后应立即结束脚本。但通过另一个PowerShell脚本用以下代码调用它时:
write-host "launching" $process = Start-Process -FilePath "powershell.exe" -ArgumentList "-Command","`"& { . 'c:\startnotepad.ps1'; }`"" -Wait -Passthru -ErrorAction Stop write-host "launched"
我必须手动关闭notepad.exe才能看到"launched"输出——明明startnotepad.ps1已经执行完毕,为什么会出现这种等待?
UPDATE
我尝试改用以下代码,它似乎能按预期工作,我会进一步测试并查看评论/答案:
$ret = Invoke-Command -ScriptBlock { . 'c:\startnotepad.ps1'; } -ErrorAction Stop;
UPDATE 2 - 测试变量冲突
修改c:\startnotepad.ps1内容如下:
$animal = "cat" function StartNotepad { try { Start-Process c:\windows\notepad.exe -Wait return 11 } catch { return 22 } }
宿主脚本内容:
$animal = "dog" $ret = Invoke-Command -ScriptBlock { . 'c:\startnotepad.ps1'; return StartNotepad; } -ErrorAction Stop write-host "Animal is $animal and return code is $ret"
输出结果为cat,这是否说明变量不存在冲突?
解答
1. 原问题:为什么调用时会等待记事本关闭?
你用Start-Process powershell.exe -Wait指定了-Wait参数,这个参数的作用是让父PowerShell进程等待你启动的子powershell.exe进程完全退出,而非等待startnotepad.ps1脚本执行完毕。
核心问题出在子PowerShell进程的行为上:当你用-Command "& { . 'c:\startnotepad.ps1'; }"启动子PowerShell时,默认情况下子PowerShell会附加到父进程的控制台窗口,且未指定-NoNewWindow或-WindowStyle的Start-Process会让notepad与子PowerShell共享控制台关联,导致子PowerShell进程不会立即退出,必须等notepad关闭后才会终止。
如果想让父脚本不等待notepad,只需让子PowerShell启动notepad后立刻退出,可修改调用命令:
write-host "launching" $process = Start-Process -FilePath "powershell.exe" -ArgumentList "-Command","`"& { Start-Process c:\windows\notepad.exe; exit; }`"" -Wait -Passthru -ErrorAction Stop write-host "launched"
2. 变量冲突测试的结果说明
你的输出是Animal is cat and return code is 11,这说明变量存在冲突——因为本地执行Invoke-Command的脚本块时,默认是在当前宿主脚本的会话中运行,. 'c:\startnotepad.ps1'会直接在当前会话中加载变量和函数,覆盖了宿主脚本里的$animal = "dog"。
若要避免变量冲突,可通过Start-Process启动独立的PowerShell进程,或用Invoke-Command创建新会话执行(如指定-Session参数),也可在脚本块内使用局部变量。
内容的提问来源于stack exchange,提问作者Captain_Planet

