powershell 如何在ScriptBlock中设置$_ get?

yvgpqqbh  于 2023-02-19  发布在  Shell
关注(0)|答案(3)|浏览(174)

我希望在脚本中有一个函数,可以使用作为 predicate 或Where-Object传入的ScriptBlock。
我会写

cat .\.gitignore | Where-Object { $_.contains('pp') }

这是可行的如:

$f =  { $_.contains('pp') }; cat .gitignore | Where-Object $f

无论多么艰难

$f.Invoke( 'apple' )

导致
方法调用异常:使用“1”个参数调用“Invoke”时发生异常:“不能对空值表达式调用方法。
而我期望的是True,所以很明显$_没有设置。
我也是

$ff = { echo "args: $args`nauto: $_" }; $ff.Invoke( 'apple' )

产出

args: apple
auto:

因此$_显然没有设置好。

'apple' | %{ $_.contains('pp') }

可以,但我希望脚本块是一个变量

$f = { $_.contains('pp') }; 'apple' | %$f

是编译错误。

**tl;dr:**那么我如何在我调用的脚本块中设置/传递$_的值呢?

31moq8wy

31moq8wy1#

请注意,此答案仅涉及如何在脚本块的process块的上下文中填充$_。其他用例可在about_PSItem文档中找到

Script Blockprocess块的上下文中,$_$PSItem)变量被自动填充并且表示来自管道的每个元素,即:

$f = { process { $_.contains('pp') }}
'apple' | & $f # True

但是,您可以使用ScriptBlock Class中的InvokeWithContext method实现相同的效果:

$f = { $_.contains('pp') }
$f.InvokeWithContext($null, [psvariable]::new('_', 'apple')) # True

请注意,此方法**始终返回[Collection1](https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.collection-1?view=net-7.0)**。输出未枚举。 值得注意的是,[zett42](https://stackoverflow.com/users/7571258/zett42)指出,通过其方法或通过调用操作符&`调用的脚本块的作用域规则仍然适用。
脚本块能够看到父作用域变量(不包括Remoting):

$foo = 'hello'
{ $foo }.Invoke() # hello

但无法更新它们:

$foo = 'hello'
{ $foo = 'world' }.Invoke()
$foo # hello

除非使用作用域修饰符(仅适用于Value Types):

$foo = 'hello'
{ $script:foo = 'world' }.Invoke()
$foo # world

或通过点采购操作员.

$foo = 'hello'
. { $foo = 'world' }
$foo # world

# still applies with pipelines!
$foo = 'hello'
'world' | . { process { $foo = $_ }}
$foo # world

有关详细信息,请参见**about Scopes**。

62o28rlo

62o28rlo2#

**最好避免使用.Invoke()方法(及其变体.InvokeReturnAsIs().InvokeWithContext())在PowerShell代码中执行script block a *,因为它在几个方面更改了调用的语义a有关详细信息,请参阅this answer

虽然PowerShell惯用的等价物是&(调用运算符),但在这里还 * 不够 *,因为您希望在脚本块中定义自动$_变量。

基于输入定义$_最简单的方法确实是ForEach-Object(它的一个内置别名是**%**):

$f = { $_.contains('pp') }
ForEach-Object -Process $f -InputObject 'apple'  # -> $true

然而,请注意,-InputObject仅对 * 单个 * 输入对象有效(尽管您可以传递数组/集合,在这种情况下$_将其作为 * 一个整体 * 引用);要提供多个,请使用管道:

'apple', 'pear' | ForEach-Object $f  # $true, $false

# Equivalent, with alias
'apple', 'pear' | % $f

相反,如果你的意图只是让你的脚本块接受 * 参数 *,你根本不需要$_,你可以简单地让你的脚本要么 * 正式声明参数*要么使用自动$args变量,它包含所有(未绑定的)位置参数:

# With $args: $args[0] is the first positional argument.
$f = { $args[0].contains('pp') }
& $f 'apple'

# With declared parameter.
$f = { param([string] $fruit) $fruit.contains('pp') }
& $f 'apple'

有关参数声明语法的更多信息,请参见概念性about_Functions帮助主题(脚本块基本上是未命名的function,并且只能在脚本块中使用param(...)声明样式)。

9o685dep

9o685dep3#

我通过将$f Package 为()使其工作,如下所示

$f = { $_.contains('pp') }; 'apple' | %($f)

或者(感谢@zett42)在%$之间放置一个空格,如

$f = { $_.contains('pp') }; 'apple' | % $f

甚至可以从变量传入值

$f = { $_.contains('pp') }; $a = 'apple'; $a | %($f)

或者在If-语句中使用它

$f = { $_.contains('pp') }; $a = 'apple'; If ( $a | %($f) ){ echo 'yes' }

所以看起来$_只能通过"管道“(aka \)来设置吗?但是,为什么要这样做,它是如何工作的,以及是否可以通过.invoke()来完成,我都不知道。如果有人能解释这一点,请解释。
What does $_ mean in PowerShell?和相关文档来看,$PSItem似乎确实是一个更好的名称,因为它不像Perl's $_

相关问题