[英]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
相同,因为echo
是Write-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
表明$input
是System.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
是:
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 的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.