当我使用cmd
和管道到使用powershell
读取输入的ReadLine()
时,它按预期接受管道输入:
C:\Users\ohnob>ECHO hi|powershell -Command "write-host $host.UI.ReadLine()"
hi
hi
但是,当我使用cmd
和管道使用powershell
ReadLineAsSecureString()
时,它会挂起,直到我输入到终端:
C:\Users\ohnob>ECHO hi|powershell -Command "write-host $host.UI.ReadLineAsSecureString()"
当会话是交互式的时候,我需要读作一个安全的字符串(使用星号)。但是当有输入管道时我需要读取管道输入。我如何在powershell
中实现这一目标?我希望我能够以某种方式检测stdin是否是管道然后有条件地使用ReadLine()
而不是ReadLineAsSexcureString()
,但我不知道如何在powershell中获得标准输入的句柄 - 我只能访问此PSHostUserInterface
对象。
编辑:澄清一下,当我使用ReadLineAsSecureString()
或ReadLine()
时,我希望它从输入中读取一行。这允许它被多次使用,并且用户可以为脚本提供多个值。我希望任何答案都是我使用的功能的替代品,除非我没有遇到的问题。谢谢!
Jeroen Mostert在评论中提出了一些好处,特别是出于安全原因,$host.UI.ReadLineAsSecureString()
/ Read-Host -AsSecureString
可能不接受管道输入[1]。
因此,您必须明确区分接收管道输入和不接收任何:
$Input
变量提供对从外部传输的stdin输入的访问。$MyInvocation.ExpectingInput
通常表示是否存在管道输入。如果有管道输入,则将其第一行[2]传递给ConvertTo-SecureString
;
如果没有,请致电Read-Host -AsSecureString
:
$secStr = if ($MyInvocation.ExpectingInput) {
# Alternatively, use $firstLine = [console]::ReadLine() - thanks, @binki
$firstLine = $($null = $Input.MoveNext(); $Input.Current)
ConvertTo-SecureString -AsPlainText -Force $firstLine
} else {
Read-Host -AsSecureString -Prompt 'Enter secret'
}
作为从cmd.exe
调用的命令行,它也输出结果:
C:>echo hi| powershell -Command "$secStr = if ($MyInvocation.ExpectingInput) { $firstLine = $($null = $Input.MoveNext(); $Input.Current); ConvertTo-SecureString -AsPlainText -Force $firstLine } else { Read-Host -AsSecureString -Prompt 'Enter secret' }l$secStr"
但是请注意,通过设计将字符串安全打印为System.Security.SecureString
,这就是你所能看到的。
[1]我猜测缺乏这方面的文件。将明文字符串管道化为Read-Host -AsSecureString
肯定有更大的不安全潜力:被管道传输的纯文本字符串可能会在某处保持,至少假设,如果你使用像MyCustomEcho.exe secret | ...
这样的东西,过程'命令行将反映秘密信息。
无论哪种方式,将纯文本传递给ConvertTo-SecureString -AsPlainText
始终是一个选项,其中传递-Force
的额外需求也表明PowerShell考虑使用未以交互方式键入的纯文本输入。
[2]自动$Input
变量是一个枚举器(类型),它根据需要枚举来自stdin的行。不支持$Input[0]
等索引访问;相反,你必须使用.MoveFirst()
来启动枚举,然后访问.Current
属性以获得第一行。这样,剩余的元素不被消耗,后来使用$Input
产生剩余的行。
围绕$(...)
的$null = $Input.MoveNext(); $Input.Current
的唯一原因是,为了概念清晰,这两个语句可以包含在返回第一行的单个语句中;首先使用$null = $Input.MoveNext()
和$firstLine = $Input.Current
也很好。