隐藏 Invoke-WebRequest 的进度

92
我怎样才能隐藏 Invoke-WebRequest 的进度显示?我要做许多连续请求,并且已经有了我自己的 Write-Progress 显示,因此每次弹出内置显示都会干扰我的显示。
我使用从 Invoke-WebRequest 自动创建的 mshtml 结果(IE COM 对象),因此除非有人提供如何从 WebClient 请求获取 mshtml 对象的说明,否则我无法切换到 WebClient 或类似的东西。

5
请注意,隐藏进度条时,性能会提高数个数量级。我等了几分钟后才取消了一个小于300MB的下载任务。当关闭进度条后,该任务在几秒钟内完成。相关链接:进度条会严重影响cmdlet的性能 - Janis Veinbergs
3个回答

132

使用 $progressPreference 变量,它默认应该具有值 'Continue',除非您在其他地方进行了编辑,这告诉 Powershell 显示进度条。由于您提到您有自己的自定义进度显示,我会在命令完成后立即重置它。例如:

$ProgressPreference = 'SilentlyContinue'    # Subsequent calls do not display UI.
Invoke-WebRequest ...
$ProgressPreference = 'Continue'            # Subsequent calls do display UI.
Write-Progress ...

有关偏好变量的更多信息,请参见about_preference_variables。以下是$ProgressPreference的条目:

$ProgressPreference
-------------------
Determines how Windows PowerShell responds to progress updates 
        generated by a script, cmdlet or provider, such as the progress bars
        generated by the Write-Progress cmdlet. The Write-Progress cmdlet 
        creates progress bars that depict the status of a command.

        Valid values:
          Stop:               Does not display the progress bar. Instead,
                                it displays an error message and stops executing.

          Inquire:            Does not display the progress bar. Prompts
                                for permission to continue. If you reply
                                with Y or A, it displays the progress bar.

          Continue:           Displays the progress bar and continues with
          (Default)             execution.

          SilentlyContinue:   Executes the command, but does not display
                                the progress bar.

26
如果其他人也遇到了这个问题......在调用其他脚本块时,您可能需要明确指定$global:progressPreference = 'silentlyContinue' - Chris Baxter
10
ن½؟用$oldProgressPreference = $progressPreference; $progressPreference = 'SilentlyContinue';,然هگژç¨چهگژن½؟用$progressPreference = $oldProgressPreferenceن¼ڑن؟‌ç•™ه…ˆه‰چçڑ„设置,هڈ¯ن»¥وڈگé«کè„ڑوœ¬é“¾وژ¥و—¶çڑ„ن¸€è‡´و€§م€‚ - Siavas
1
@ChrisBaxter 谢谢!没有 $global 部分时,当我直接在我的 CI 配置中编写命令时,它对我来说运行得很好,但是一旦我将其移动到 PowerShell 脚本中,它就停止工作了。设置全局变量立即解决了这个问题! - Ben Baron

8

以下是一个可复用的函数,用于临时隐藏任何脚本块的进度,并在脚本块结束时自动恢复进度首选项,即使脚本块抛出异常(终止脚本错误),也可以恢复。

# Create an in-memory module so $ScriptBlock doesn't run in new scope
$null = New-Module {
    function Invoke-WithoutProgress {
        [CmdletBinding()]
        param (
            [Parameter(Mandatory)] [scriptblock] $ScriptBlock
        )

        # Save current progress preference and hide the progress
        $prevProgressPreference = $global:ProgressPreference
        $global:ProgressPreference = 'SilentlyContinue'

        try {
            # Run the script block in the scope of the caller of this module function
            . $ScriptBlock
        }
        finally {
            # Restore the original behavior
            $global:ProgressPreference = $prevProgressPreference
        }
    }
}

使用示例:

Invoke-WithoutProgress {
    # Here $ProgressPreference is set to 'SilentlyContinue'
    Invoke-WebRequest ...
}

# Now $ProgressPreference is restored
Write-Progress ...

注:

  • New-Module 调用是为了确保传递给 Invoke-WithoutProgress 的脚本块不在新作用域中运行(允许直接修改周围变量,类似于 ForEach-Object 的脚本块)。有关更多信息,请参见此答案

只是想知道,这个技巧是否也会吞掉异常,这可能是不希望发生的。我喜欢这种方法(在C++和Python中也使用过类似的lambda表达式),但是吞掉异常作为副作用可能应该被注意到。不过还是感谢您展示了这个技巧。 - 0xC0000022L
@0xC0000022L 不,它不会吞噬异常,因为函数中没有catch块。finally块只是确保在任何情况下都恢复了$ProgressPreference变量,但是异常仍然会在finally块运行后沿着调用堆栈向上冒泡。 - zett42
1
好主意,谢谢! - Endrju

0

PowerShell 7.4已发布,并为所有的cmdlet添加了-ProgressAction作为一个常见参数。这意味着您可以在Invoke-WebRequest中使用-ProgressAction SilentlyContinue,轻松隐藏其进度。


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