简体   繁体   English

在 Powershell 中不引用 $input,其值为“Current”

[英]Not quoting $input in Powershell, and its value is "Current"

I was writing a PowerShell script, and forgot to quote $input when piping to a command.我正在编写一个 PowerShell 脚本,并在管道传输到命令时忘记引用$input The command unexpectedly appeared to receive the string Current on stdin, so I investigated further and typed the following:该命令意外地在 stdin 上接收到字符串Current ,因此我进一步调查并输入了以下内容:

PS C:\> echo $input

Current
-------



PS C:\> echo "$input"

The difference in output between these two statements confuses me, and I'm not sure what to search for to understand this better.这两个语句之间的输出差异让我感到困惑,我不确定要搜索什么才能更好地理解这一点。 I would have expected both to output nothing, like the second command.我希望两者都不会输出任何内容,就像第二个命令一样。

What does "Current" mean? “当前”是什么意思? Is this a general quote behaviour, or something specific to $input ?这是一般的引用行为,还是特定于$input的行为?

Building on the helpful comments:基于有用的评论:

  • The automatic $input variable is managed by PowerShell , and represents input from the pipeline in appropriate contexts. 自动$input变量由 PowerShell管理,并在适当的上下文中表示来自管道的输入

  • As such, use of $input as a user variable, for custom purposes, should be avoided .因此,应避免出于自定义目的将$input用作用户变量。

  • To use $input , do not use it with Write-Output (whose built-in alias is echo ): either use $input as is in the pipeline, or, to force instant enumeration, use @($input) .要使用$input不要将它与Write-Output (其内置别名是echo )一起使用:要么在管道中使用$input 要么强制即时枚举,使用@($input)


As for what you tried :至于你尝试了什么:

 echo $input

This is effectively the same as Write-Output $input , given that echo is a built-in alias of the Write-Output cmdlet.这实际上与Write-Output $input相同,因为echoWrite-Output cmdlet 的内置别名。

PowerShell's for-display output-formatting system uses a reflection-based approach for types that (a) do not have predefined formatting data associated with them and (b) are neither strings no (quasi) primitive .NET types such as numbers . PowerShell 的显示输出格式化系统对以下类型使用基于反射的方法:(a) 没有与之关联的预定义格式化数据,并且 (b) 既不是字符串也不是(准)原始 .NET 类型,例如数字

Instances of such types are represented by their public properties , and instances up to 4 properties are implicitly formatted with Format-Table , instances with 5 or more properties implicitly with Format-List .此类类型的实例由其公共属性表示,最多 4 个属性的实例使用Format-Table隐式格式化,具有 5 个或更多属性的实例使用Format-List隐式格式化。

The automatic $input variable is an enumerator that represents the current context's pipeline input.自动$input变量是一个枚举器,表示当前上下文的管道输入。

$input.GetType().FullName reveals that $input is an instance of the System.Collections.ArrayList+ArrayListEnumeratorSimple class. $input.GetType().FullName表明$inputSystem.Collections.ArrayList+ArrayListEnumeratorSimple类的一个实例。

It isn't directly obvious, but this - non-public - class is what the System.Collections.ArrayList 's .GetEnumerator() method returns, which is an enumerator class that implements the System.Collections.IEnumerator interface, which you can verify as follows: $input.GetType().GetInterfaces()这不是很明显,但是这个 - 非公共 - 类是System.Collections.ArrayList.GetEnumerator()方法返回的,它是一个实现System.Collections.IEnumerator接口的枚举器类,您可以验证如下: $input.GetType().GetInterfaces()

This interface has one public property, .Current , which you can verify as follows, using PowerShell's intrinsic psobject property : $input.psobject.Properties.Name此接口有一个公共属性.Current ,您可以使用 PowerShell 的固有psobject属性按如下方式验证它: $input.psobject.Properties.Name

Therefore, Write-Output $input implicitly uses Format-Table to render the $input instance's one and only property, .Current , which is what you saw.因此, Write-Output $input隐式使用Format-Table来呈现$input实例的唯一属性.Current ,这就是您所看到的。

Irrespective of how many elements the enumerator represents - none outside a pipeline - the .Current property value is $null and therefore not showing in the output, since the .MoveNext() method isn't being called in this case.无论枚举器代表多少个元素——管道外没有元素—— .Current属性值为$null ,因此不会显示在输出中,因为在这种情况下不会.MoveNext()方法。

Example:例子:

function foo { Write-Output $input }
1..3 | foo  # !! Still prints a table with an empty "Current" column.

In short: Passing $input as-is to Write-Output is virtually pointless .简而言之:按原样将$input传递给Write-Output实际上毫无意义

  • The to-display output is always unhelpful, as shown above.如上所示,要显示的输出总是无用的。

  • In the pipeline: Write-Output $input | & someCommand在管道中: Write-Output $input | & someCommand Write-Output $input | & someCommand is: Write-Output $input | & someCommand是:

    • equally useless if someCommand is an external program (executable), because it is also the for-display representation that is sent (as a stream of strings (lines)), given that PowerShell only "speaks text" when communicating with external programs, as of PowerShell 7.3.1如果someCommand是一个外部程序(可执行)同样没用,因为它也是发送的用于显示的表示形式(作为字符串流(行)),因为 PowerShell 在与外部程序通信时仅“说文本”,如PowerShell 7.3.1 的
    • does work if someCommand is a PowerShell command (whether built-in or custom), but there is no benefit to using Write-Output in that scenario just using $input by itself , taking advantage of PowerShell's implicit output behavior , is both more concise and more efficient.如果someCommand是一个PowerShell命令(无论是内置的还是自定义的),它确实有效,但是在那种情况下使用Write-Output没有任何好处,只是使用$input本身,利用 PowerShell 的隐式输出行为,既更简洁又更高效。

Given the above, the current behavior (which analogously also affects Write-Host ) - as of PowerShell 7.3.1 - could be considered a bug , and has been reported in GitHub issue #18826 .鉴于上述情况,从 PowerShell 7.3.1 开始的当前行为(类似地也影响Write-Host )可以被视为一个错误,并且已在GitHub 问题 #18826中报告。

Examples:例子:

# !! USELESSS, because findstr.exe receives the *for-display*
# !! representation of the enumerator, line by line.
# Note: The findstr.exe call simply passes each line it receives through.
1..3 | & { Write-Output $input | findstr.exe '^'  }

# OK, due to using a *PowerShell* command,
# but there's no good reason to use Write-Output here.
function foo { Write-Output $input | ForEach-Object { "[$_]" } }
1..3 | foo # -> '[1]', '[2]', '[3]'

# Preferred approach, because it is both simpler and more efficient:
# *implicit* output of $input, which causes enumeration.
function foo { $input | ForEach-Object { "[$_]" } }
1..3 | foo # same as above

 echo "$input"

Enclosing $input in "..." , ie in an expandable string , forces it to be stringified .$input包含在"..."中,即在可扩展字符串中,强制将其字符串化

Unlike when you pass $input to Write-Output , using it inside "..." :与将$input传递给Write-Output不同,在"..."中使用它:

  • causes enumeration of the enumerator (in essence, repeatedly calling .MoveNext() on it while it returns $true , and then accessing its .Current property)导致枚举器的枚举(本质上,在它返回$true时重复调用它的.MoveNext() ,然后访问它的.Current属性)

  • with the resulting (stringified) objects joined with spaces, which is how PowerShell stringifies arrays (collections)结果(字符串化)对象用空格连接,这就是 PowerShell 字符串化数组(集合)的方式

Outside of a pipeline , $input is an enumerator without elements , which causes "$input" to evaluate to the empty string , which, in terms of display output results in an empty line .在管道之外$input是一个没有元素的枚举器,这会导致"$input"的计算结果为空字符串,就显示输出结果而言,这会导致空行

An example of how it acts in a pipeline :它如何在管道中起作用的示例:

'one', 2 | & { "[$input]" }

Output:输出:

[one 2]

That is, the (stringified) input objects were joined with a single space.也就是说,(字符串化的)输入对象是用一个空格连接的。

(A single space is the default separator. Technically, you can specify a different one via the $OFS preference variable , although that is rarely done in pratice). (单个空格是默认分隔符。从技术上讲,您可以通过$OFS首选项变量指定不同的分隔符,尽管在实践中很少这样做)。

In short: While "$input" does force enumeration, it may not do what you want , given that:简而言之:虽然"$input"确实强制枚举,但它可能不会做你想做的事情,因为:

  • for non-string input, the original type identity is lost .对于非字符串输入,原始类型标识丢失

  • even for string input, multiple input strings end up as one, single-line string , with the input strings separated with spaces, as discussed above.即使对于字符串输入,多个输入字符串最终会变成一个单行字符串,输入字符串用空格分隔,如上所述。

Examples:例子:

# OK: Input strings are passed one by one (as lines) to findstr.exe
'foo', 'bar' | & { $input | findstr.exe '^' }

# !! DIFFERENT: "$input" forces collecting the inputs in an array,
# !! which is then stringified; therefore a *single string* containing
# !! 'foo bar' is sent.
# !! Using Write-Output "$input"` would behave the same.
'foo', 'bar' | & { "$input" | findstr.exe '^' }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM