PowerShell 在 运行 Import-Module 时忽略 Write-Verbose
PowerShell ignoring Write-Verbose while running Import-Module
为了提出问题,我将这个简单的脚本保存为 PowerShell 模块 (test.psm1
)
Write-Verbose 'Verbose message'
在现实生活中,它包括导入附加功能的命令,但目前无关紧要。
如果我运行 Import-Module .\test.psm1 -Verbose -Force
我只得到
VERBOSE: Loading module from path 'C:\tmp\test.psm1'.
我的Write-Verbose
被忽略了
我尝试添加 cmdletbinging
但它也没有用。
[cmdletbinding()]
param()
Write-Verbose 'Verbose message'
知道如何在导入 PowerShell 模块时提供详细输出吗?
P.S。我不想总是显示详细信息,但只有在指定 -Verbose
时才显示。这是我对这两种不同情况的预期输出:
PS C:\> Import-Module .\test.psm1 -Verbose -Force # with verbose output
VERBOSE: Loading module from path 'C:\tmp\test.psm1'.
VERBOSE: Verbose message
PS C:\> Import-Module .\test.psm1 -Force # without verbose output
PS C:\>
这是一个有趣的情况。我有一个理论,但如果有人能证明我是错的,我会非常高兴。
简短的回答:你可能无法通过只玩 -Verbose
来做你想做的事。可能有一些解决方法,但最短路径可能是设置 $VerbosePreference
.
首先我们要了解lifetime of a module什么时候导入:
When a module is imported, a new session state is created for the
module, and a System.Management.Automation.PSModuleInfo
object is
created in memory. A session-state is created for each module that is
imported (this includes the root module and any nested modules). The
members that are exported from the root module, including any members
that were exported to the root module by any nested modules, are then
imported into the caller's session state. [..] To send output to the host, users should run the Write-Host
cmdlet.
最后一行是指向我解决方案的第一个提示:导入模块时,会创建一个新的 session state,但只有导出的元素会附加到全局会话状态。这意味着 test.psm1
代码在不同于您 运行 Import-Module
的会话中执行,因此不会传播与该单个命令相关的 -Verbose
选项。
相反,这是我的假设,因为我没有在文档中找到它,来自全局会话状态的配置对所有子会话都是可见的。为什么这很重要?因为有两种方式可以开启冗长:
-Verbose
选项,在这种情况下不起作用,因为它对命令来说是本地的
$VerbosePreference
,使用首选项变量设置整个会话的详细程度。
我尝试了第二种方法并且它起作用了,尽管不是那么优雅。
$VerbosePreference = "Continue" # print all the verbose messages, disabled by default
Import-Module .\test.psm1 -Force
$VerbosePreference = "SilentlyContinue" # restore default value
现在一些注意事项:
在 Import-Module
命令上指定 -Verbose
是多余的
您仍然可以使用
覆盖模块脚本中的详细配置
Write-Verbose -Message "Verbose message" -Verbose:$false
正如@Vesper 指出的那样,$false
将始终抑制 Write-Verbose
输出。相反,您可能希望使用先前检查中分配的布尔变量对该选项进行参数化。类似于:
if (...)
{
$forceVerbose=$true
}
else
{
$forceVerbose=$false
}
Write-Verbose -Message "Verbose message" -Verbose:$forceVerbose
可能还有其他侵入性较小的解决方法(例如以 Write-Host
为中心),甚至是真正的解决方案。正如我所说,这只是一个理论。
Marco Luzzara 的回答很准确(在我看来值得赏金)关于模块 运行 处于其自己的会话状态,并且由设计您无法访问这些变量。
设置 $VerbosePreference
并恢复它的替代解决方案是让您的模块采用专门用于此目的的参数。您通过尝试将 [CmdletBinding()]
添加到您的模块来稍微触及这一点;问题是你无法通过 named 参数传递,只有未命名的参数,通过 Import-Module -ArgumentList
,所以你不能专门传递 $true
for -Verbose
.
相反,您可以指定自己的参数并使用它。
(psm1)
[CmdletBinding()]param([bool]$myverbose)
Write-Verbose "Message" -Verbose:$myverbose
后跟:
Import-Module test.psm1 -Force -ArgumentList $true
在上面的例子中,它只适用于一个特定的命令,你每次都设置 -Verbose:$myverbose
。
但是你可以将它应用到模块的 $VerbosePreference
:
[CmdletBinding()]param([bool]$myverbose)
$VerbosePreference = if ($myverbose) { 'Continue' } else { 'SilentlyContinue' }
Write-Verbose "Message"
这种方式贯穿始终。
在这一点上我应该提到我所展示的缺点:您可能会注意到我没有在 Import-Module
调用中包含 -Verbose
,那是因为它没有改变模块内部的行为。无论 Import-Module
.
上的 -Verbose
设置如何,内部的详细消息将完全根据您传入的参数显示
一个一体式解决方案然后回到 Marco 的回答:在调用方一侧操纵 $VerbosePreference
。我认为这是使两种行为保持一致的唯一方法,但前提是您不使用 -Verbose
切换 Import-Module
来覆盖。
另一方面,在范围内,就像在可以采用 -Verbose
的高级函数内一样,设置开关会更改 $VerbosePreference
的本地值。这会导致我们在自己的函数中包装 Import-Module
:
function Import-ModuleVerbosely {
[CmdletBinding()]
param($Name, [Switch]$Force)
Import-Module $Name -Force:$Force
}
太棒了!现在我们可以调用 Import-ModuleVerbosely test.psm1 -Force -Verbose
。但是……没用。 Import-Module
确实识别了详细设置,但这次没有将其放入模块中。
虽然我没能找到查看它的方法,但我怀疑这是因为变量设置为 Private(尽管 Get-Variable
似乎另有说明)所以该值不这次成功。不管是什么原因……我们可以回去让我们的模块接受一个值。这次为了方便使用,还是改成同一个类型吧:
(psm1)
[CmdletBinding()]param([System.Management.Automation.ActionPreference]$myverbose)
if ($myverbose) { $VerbosePreference = $myverbose }
Write-Verbose "message"
那我们换个函数:
function Import-ModuleVerbosely {
[CmdletBinding()]
param($Name, [Switch]$Force)
Import-Module $Name -Force:$Force -ArgumentList $VerbosePreference
}
嘿,现在我们有进展了!但是..有点笨重不是吗?
你可以更进一步,为 Import-Module
创建一个完整的代理函数,然后为它创建一个名为 Import-Module
的别名来替换真实的。
最终你会尝试做一些不受真正支持的事情,所以这取决于你想走多远。
为了提出问题,我将这个简单的脚本保存为 PowerShell 模块 (test.psm1
)
Write-Verbose 'Verbose message'
在现实生活中,它包括导入附加功能的命令,但目前无关紧要。
如果我运行 Import-Module .\test.psm1 -Verbose -Force
我只得到
VERBOSE: Loading module from path 'C:\tmp\test.psm1'.
我的Write-Verbose
被忽略了
我尝试添加 cmdletbinging
但它也没有用。
[cmdletbinding()]
param()
Write-Verbose 'Verbose message'
知道如何在导入 PowerShell 模块时提供详细输出吗?
P.S。我不想总是显示详细信息,但只有在指定 -Verbose
时才显示。这是我对这两种不同情况的预期输出:
PS C:\> Import-Module .\test.psm1 -Verbose -Force # with verbose output
VERBOSE: Loading module from path 'C:\tmp\test.psm1'.
VERBOSE: Verbose message
PS C:\> Import-Module .\test.psm1 -Force # without verbose output
PS C:\>
这是一个有趣的情况。我有一个理论,但如果有人能证明我是错的,我会非常高兴。
简短的回答:你可能无法通过只玩 -Verbose
来做你想做的事。可能有一些解决方法,但最短路径可能是设置 $VerbosePreference
.
首先我们要了解lifetime of a module什么时候导入:
When a module is imported, a new session state is created for the module, and a
System.Management.Automation.PSModuleInfo
object is created in memory. A session-state is created for each module that is imported (this includes the root module and any nested modules). The members that are exported from the root module, including any members that were exported to the root module by any nested modules, are then imported into the caller's session state. [..] To send output to the host, users should run theWrite-Host
cmdlet.
最后一行是指向我解决方案的第一个提示:导入模块时,会创建一个新的 session state,但只有导出的元素会附加到全局会话状态。这意味着 test.psm1
代码在不同于您 运行 Import-Module
的会话中执行,因此不会传播与该单个命令相关的 -Verbose
选项。
相反,这是我的假设,因为我没有在文档中找到它,来自全局会话状态的配置对所有子会话都是可见的。为什么这很重要?因为有两种方式可以开启冗长:
-Verbose
选项,在这种情况下不起作用,因为它对命令来说是本地的$VerbosePreference
,使用首选项变量设置整个会话的详细程度。
我尝试了第二种方法并且它起作用了,尽管不是那么优雅。
$VerbosePreference = "Continue" # print all the verbose messages, disabled by default
Import-Module .\test.psm1 -Force
$VerbosePreference = "SilentlyContinue" # restore default value
现在一些注意事项:
在
Import-Module
命令上指定-Verbose
是多余的您仍然可以使用
覆盖模块脚本中的详细配置Write-Verbose -Message "Verbose message" -Verbose:$false
正如@Vesper 指出的那样,
$false
将始终抑制Write-Verbose
输出。相反,您可能希望使用先前检查中分配的布尔变量对该选项进行参数化。类似于:if (...) { $forceVerbose=$true } else { $forceVerbose=$false } Write-Verbose -Message "Verbose message" -Verbose:$forceVerbose
可能还有其他侵入性较小的解决方法(例如以
Write-Host
为中心),甚至是真正的解决方案。正如我所说,这只是一个理论。
Marco Luzzara 的回答很准确(在我看来值得赏金)关于模块 运行 处于其自己的会话状态,并且由设计您无法访问这些变量。
设置 $VerbosePreference
并恢复它的替代解决方案是让您的模块采用专门用于此目的的参数。您通过尝试将 [CmdletBinding()]
添加到您的模块来稍微触及这一点;问题是你无法通过 named 参数传递,只有未命名的参数,通过 Import-Module -ArgumentList
,所以你不能专门传递 $true
for -Verbose
.
相反,您可以指定自己的参数并使用它。
(psm1)
[CmdletBinding()]param([bool]$myverbose)
Write-Verbose "Message" -Verbose:$myverbose
后跟:
Import-Module test.psm1 -Force -ArgumentList $true
在上面的例子中,它只适用于一个特定的命令,你每次都设置 -Verbose:$myverbose
。
但是你可以将它应用到模块的 $VerbosePreference
:
[CmdletBinding()]param([bool]$myverbose)
$VerbosePreference = if ($myverbose) { 'Continue' } else { 'SilentlyContinue' }
Write-Verbose "Message"
这种方式贯穿始终。
在这一点上我应该提到我所展示的缺点:您可能会注意到我没有在 Import-Module
调用中包含 -Verbose
,那是因为它没有改变模块内部的行为。无论 Import-Module
.
-Verbose
设置如何,内部的详细消息将完全根据您传入的参数显示
一个一体式解决方案然后回到 Marco 的回答:在调用方一侧操纵 $VerbosePreference
。我认为这是使两种行为保持一致的唯一方法,但前提是您不使用 -Verbose
切换 Import-Module
来覆盖。
另一方面,在范围内,就像在可以采用 -Verbose
的高级函数内一样,设置开关会更改 $VerbosePreference
的本地值。这会导致我们在自己的函数中包装 Import-Module
:
function Import-ModuleVerbosely {
[CmdletBinding()]
param($Name, [Switch]$Force)
Import-Module $Name -Force:$Force
}
太棒了!现在我们可以调用 Import-ModuleVerbosely test.psm1 -Force -Verbose
。但是……没用。 Import-Module
确实识别了详细设置,但这次没有将其放入模块中。
虽然我没能找到查看它的方法,但我怀疑这是因为变量设置为 Private(尽管 Get-Variable
似乎另有说明)所以该值不这次成功。不管是什么原因……我们可以回去让我们的模块接受一个值。这次为了方便使用,还是改成同一个类型吧:
(psm1)
[CmdletBinding()]param([System.Management.Automation.ActionPreference]$myverbose)
if ($myverbose) { $VerbosePreference = $myverbose }
Write-Verbose "message"
那我们换个函数:
function Import-ModuleVerbosely {
[CmdletBinding()]
param($Name, [Switch]$Force)
Import-Module $Name -Force:$Force -ArgumentList $VerbosePreference
}
嘿,现在我们有进展了!但是..有点笨重不是吗?
你可以更进一步,为 Import-Module
创建一个完整的代理函数,然后为它创建一个名为 Import-Module
的别名来替换真实的。
最终你会尝试做一些不受真正支持的事情,所以这取决于你想走多远。