如何运行一个不显示窗口的PowerShell脚本?

227

如何在不显示窗口或任何其他标志给用户的情况下运行PowerShell脚本?

换句话说,脚本应该在后台静默运行,用户看不到任何迹象。

额外加分答案不使用第三方组件 :)


如果您对学习感兴趣,请查看以下问题:https://dev59.com/D3RB5IYBdhLWcg3wn4UL - Thomas Bratt
这个解决方案同样适用于任务计划程序:https://stackoverflow.com/a/51007810/571591 - Hilydrow
如果您绝对需要避免显示窗口,还有其他选项,例如Windows服务。 - ryanwebjackson
对于任何感兴趣的人,即使使用“-WindowStyle hidden”,窗口闪烁也是CUI应用程序已知的Windows限制(它只能像预期的那样与GUI应用程序一起工作--因此其他项目实现了pythonw.exe / javaw.exe类型的解决方案)。 它正在GitHub上被(相当)积极地讨论,有几个关于在PowerShell或操作系统级别进行潜在解决方法/修复的建议。 因此,它可能会在“某一天”得到修补。 - desseim
请注意,在组策略中使用的PowerShell登录脚本将会以隐藏窗口的形式运行。 - undefined
26个回答

5

这实际上非常有帮助,因为许多其他解决方案有点繁琐和/或不理想。这个方法非常快。我也将其修改为管理员模式运行,因为这就是我来这里的目的(没有闪烁的窗口+以管理员身份运行)。https://github.com/cherryleafroad/run-hidden - Kobato
当您在参数中使用“%USERPROFILE%”时,似乎无法正常工作。 - jamie
@jamie 我认为这是正常的行为!Process类不会扩展环境变量和用户应用程序,例如run-hidden通常也不会这样做,我相信。 - stax76
嗯,我明白了。谢谢你解释。 - jamie

5
创建一个快捷方式,调用PowerShell脚本并将运行选项设置为最小化。这将防止窗口闪烁,尽管您仍会在任务栏上看到脚本运行的瞬间。

5

这是一个有趣的演示,可以控制控制台的各种状态,包括最小化和隐藏。

Add-Type -Name ConsoleUtils -Namespace WPIA -MemberDefinition @'
   [DllImport("Kernel32.dll")]
   public static extern IntPtr GetConsoleWindow();
   [DllImport("user32.dll")]
   public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'@

$ConsoleMode = @{
 HIDDEN = 0;
 NORMAL = 1;
 MINIMIZED = 2;
 MAXIMIZED = 3;
 SHOW = 5
 RESTORE = 9
 }

$hWnd = [WPIA.ConsoleUtils]::GetConsoleWindow()

$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.MAXIMIZED)
"maximized $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.NORMAL)
"normal $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.MINIMIZED)
"minimized $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.RESTORE)
"restore $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.HIDDEN)
"hidden $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.SHOW)
"show $a"

4

我很疲倦,不断查找答案却发现结果与预期不符。

解决方案

创建一个vbs脚本来运行一个隐藏的批处理文件,然后启动powershell脚本。这个任务看起来有些愚蠢,需要创建三个文件,但是总文件大小不到2KB,并且它可以从任务管理器或手动执行时完美地运行(你看不到任何东西)。

scriptName.vbs

Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "C:\Users\leathan\Documents\scriptName.bat" & Chr(34), 0
Set WinScriptHost = Nothing

scriptName.bat

powershell.exe -ExecutionPolicy Bypass C:\Users\leathan\Documents\scriptName.ps1

scriptName.ps1

Your magical code here.

为什么你不能直接在计划任务中使用 .bat 文件?为什么需要使用 .vbs 来调用 .bat 文件?我刚刚测试了在计划任务中使用 .BAT 文件,它可以正常工作而且没有任何弹出窗口。 - shadowz1337
我忘记了为什么我没有使用计划任务,但是我能想到很多原因,但都不能保证正确,因为我忘记了。我只记得发布我的解决方案,因为我最初尝试了其他方法,但它们都不起作用。我记得计划任务不是我想要的,也许是因为它太动态了,我不知道。实际上,答案可能是我使用vbs,因为我无法使用计划任务,无论如何,这都不重要,如果您的答案不在此处,请将其作为另一个答案提交。 - Leathan
@shadowz1337 我已经弄清楚了,因为我需要再次执行它。当启动一个从调度程序隐藏的任务时,会出现各种奇怪的问题,这些问题会阻止批处理文件真正地被隐藏。这里的vbs特别修复了这个问题,有了它,您可以作为管理员用户运行隐藏的bat,否则您必须使用system或以任何用户身份运行。请参见https://superuser.com/questions/478052/windows-7-task-scheduler-hidden-setting-doesnt-work以获取详细说明。 - Leathan
我只是使用计划任务调用nircmd exec hide来运行.bat文件,然后作为管理员调用我的Powershell脚本,这完全对用户隐藏了整个过程。 - shadowz1337
不确定你所说的“如果替换第一步就无法隐藏运行”的意思是什么。不管怎样,我使用任务计划程序调用带有nircmd的.bat文件来完成。像这样“nircmd exec hide D:\Test.bat”。Nircmd 是 Nirsoft 的一个工具,只需下载并将其放入 C:\Windows 文件夹中,即可从任何位置调用它。然后,在 .bat 文件中,只需像这样调用 PowerShell 脚本- powershell.exe -ExecutionPolicy ByPass -File "D:\Test.ps1"。因此,任务计划程序使用 Nircmd 调用 .bat 文件,然后调用 Powershell 脚本。完全隐藏,没有弹出窗口。 - shadowz1337
显示剩余5条评论

2
我创建了一个小工具,将调用任何你想要无窗口启动的控制台工具传递到原始文件中:

https://github.com/Vittel/RunHiddenConsole

编译完成后,将可执行文件重命名为“<targetExecutableName>w.exe”(添加一个“w”),并将其放在原始可执行文件旁边。然后,您可以像往常一样调用powershellw.exe,并且它不会弹出窗口。
如果有人有想法如何检查创建的进程是否正在等待输入,请告诉我,我很乐意包含您的解决方案:)

1
如果您想使用MessageBox运行PowerShell脚本而不会在启动时出现任何窗口闪烁(需要编译为Winexe的EXE,而不是控制台应用程序,并且需要将任务计划程序设置为“仅在用户登录时运行”,以便对话框显示在当前桌面会话中),那么这是最佳答案。感谢您实现了这一点,powershellw.exe已经在我的愿望清单上多年了! - Carl Walsh
PS:我之前已经包含了“等待输入”的解决方案(以及一些错误修复)! - Chris
@CarlWalsh 我猜你的意思是它不是控制台应用程序,而是一个Win Forms应用程序,这是正确的。只是它不包括任何窗口。 但是项目类型应该在csproj文件中定义,打开Visual Studio后不需要设置特定的输出类型。 - Chris

1
powershell.exe -windowstyle hidden -noexit -ExecutionPolicy Bypass -File <path_to_file>

然后将运行设置为最小化

应该能够按预期工作,无需添加隐藏窗口闪烁的代码,只需要稍微延迟执行即可。


1
等待 Powershell 执行并在 vbs 中获取结果。
这是 Omegastripes 代码的改进版本 (使用 Exec() 时隐藏命令提示窗口)
将 cmd.exe 的混乱响应拆分成数组,而不是把所有内容放入难以解析的字符串中。
此外,如果在执行 cmd.exe 期间发生错误,则会在 vbs 中得知有关其发生的消息。
Option Explicit
Sub RunCScriptHidden()
    strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
    GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
    objShell.Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True
End Sub
Sub WshShellExecCmd()
    For Each objWnd In CreateObject("Shell.Application").Windows
        If IsObject(objWnd.getProperty(WScript.Arguments.Named("signature"))) Then Exit For
    Next
    Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))
    objWnd.Quit
    'objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll() 'simple solution
    Set exec = CreateObject("WScript.Shell").Exec(objParent.strCmd)
    While exec.Status = WshRunning
        WScript.Sleep 20
    Wend
    Dim err
    If exec.ExitCode = WshFailed Then
        err = exec.StdErr.ReadAll
    Else
        output = Split(exec.StdOut.ReadAll,Chr(10))
    End If
    If err="" Then
        objParent.strRes = output(UBound(output)-1) 'array of results, you can: output(0) Join(output) - Usually needed is the last
    Else
        objParent.wowError = err
    End If
WScript.Quit
End Sub
Const WshRunning = 0,WshFailed = 1:Dim i,name,objShell
Dim strCmd, strRes, objWnd, objParent, strSignature, wowError, output, exec

Set objShell = WScript.CreateObject("WScript.Shell"):wowError=False
strCmd = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass Write-Host Hello-World."
If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
RunCScriptHidden
If wowError=False Then
    objShell.popup(strRes)
Else
    objShell.popup("Error=" & wowError)
End If

1
以下是在Windows 10上的一个可行解决方案,不包括任何第三方组件。它通过将PowerShell脚本封装到VBScript中来实现。
步骤1:我们需要更改一些Windows功能,以允许VBScript运行PowerShell,并默认使用PowerShell打开.ps1文件。
- 运行并输入“regedit”。单击确定,然后允许其运行。
- 粘贴此路径“HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell”,然后按Enter。
- 现在打开右侧的条目并将值更改为0。
- 以管理员身份打开PowerShell,键入“Set-ExecutionPolicy -ExecutionPolicy RemoteSigned”,按Enter,然后用“y”确认更改,再按Enter。
步骤2:现在我们可以开始封装脚本。
- 将您的Powershell脚本另存为.ps1文件。
- 创建一个新的文本文档并粘贴此脚本。
Dim objShell,objFSO,objFile

Set objShell=CreateObject("WScript.Shell")
Set objFSO=CreateObject("Scripting.FileSystemObject")

'enter the path for your PowerShell Script
 strPath="c:\your script path\script.ps1"

'verify file exists
 If objFSO.FileExists(strPath) Then
   'return short path name
   set objFile=objFSO.GetFile(strPath)
   strCMD="powershell -nologo -command " & Chr(34) & "&{" &_
    objFile.ShortPath & "}" & Chr(34)
   'Uncomment next line for debugging
   'WScript.Echo strCMD

  'use 0 to hide window
   objShell.Run strCMD,0

Else

  'Display error message
   WScript.Echo "Failed to find " & strPath
   WScript.Quit

End If

-现在将文件路径更改为您的.ps1脚本的位置,并保存文本文档。

-现在右键单击该文件并重命名。然后将文件名扩展名更改为.vbs,然后按Enter,最后单击确定。

完成!如果您现在打开.vbs文件,应该看不到控制台窗口,而您的脚本正在后台运行。


1
一个单独的vbs文件解决方案。首先,您需要将ps脚本转换为base64字符串,将其放置在下面所示的模板变量中,并将其保存为vbs文件。运行时不会弹出PowerShell窗口。
dim EncodedCommand
EncodedCommand = "COMMAND"

pSCmd = "powershell.exe -noexit -windowstyle Hidden -executionpolicy bypass -encodedcommand " & EncodedCommand

CreateObject("WScript.Shell").Run pSCmd, 0, True

你如何捕获命令的文本输出? - pgr

0

我发现将代码编译为exe是实现这一目标最简单的方法。有许多编译脚本的方式,但你可以尝试使用ISE Steroids。

打开“Windows PowerShell ISE”,安装并运行ISESteroids:

Install-Module -Name "ISESteroids" -Scope CurrentUser -Repository PSGallery -Force

Start-Steroids

然后转到工具->将代码转换为EXE,选择“隐藏控制台窗口”,然后创建应用程序。 您可以直接从任务计划程序运行此应用程序,无需包装器或第三方应用程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接