将stdin引入Powershell流

lit*_*lit 4 powershell batch-file

在命令行上指定文件名时,以下脚本运行良好.

tail.bat
@echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
powershell "Get-Content %FILENAME% -Last %COUNT%"
Run Code Online (Sandbox Code Playgroud)

但是,我需要的是能够Get-Content从stdin 管道文本.我想编写以下内容以获取分配给项目的最后三个Subversion标记.我该怎么做才能使源Get-Content成为标准输入?

svn ls svn://ahost/arepo/aproject/tags | call tail.bat -3
Run Code Online (Sandbox Code Playgroud)

注意:我不允许tail从外面安装任何有用的工具.必须使用机器上已有的程序完成.

mkl*_*nt0 7

重写tail.bat如下:

@echo off

set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"

if "%FILENAME%"=="" (
  powershell -noprofile -command "$Input | Select-Object -Last %COUNT%"
) else (
  powershell -noprofile -command "Get-Content \"%FILENAME%\" -Last %COUNT%"
)
Run Code Online (Sandbox Code Playgroud)

这将使PowerShell读取stdin输入$Input,如果没有传递文件名参数,请回答这个问题.

例:

C:> (echo one & echo two & echo three) | tail.bat -2
two
three
Run Code Online (Sandbox Code Playgroud)

注意:

  • 虽然PowerShell通常通过管道发送并输出任何类型的对象,但它与外部世界的接口总是涉及字符串.

  • 因此,鉴于它$Input是一个表示外部 stdin输入的枚举器,我们可以确定它一个一个地枚举输入文本行(作为字符串),所以我们只需要选择感兴趣的行,这就是为什么管道是足够.Select-Object

  • 相比之下,在PowerShell中按名称读取文件需要Get-Content(顺便提一下,除了你还指定之外,它还会逐个通过管道发送输入文件的行-Raw); 因为Get-Contenttail内置的功能,通过参数-Tail(和它的别名-Last),这是所有在这里需要.

  • CAVEAT: 当PowerShell与外界交谈时,涉及输入上的字符解码和输出上的重新编码:

    • 如果您只处理ASCII编码输入(代码点介于0到127之间的单字节字符),您不必担心.

    • 否则,为痛苦的世界做好准备 - 详见下文.


解码/重新编码问题

  • 假设PowerShell可识别你输入的编码(见下文),该输出编码是不可避免的什么控制台窗口的分配编码; 不幸的是,默认情况下,这是OEM代码页(例如,美国 - 英语系统上的"DOS"代码页CP437),反映在PS中[Console]::OutputEncoding.

    • 因此,通过正确识别的输入,如果您打印到控制台,事情看起来会很好,但如果您在文件中捕获输出,您最终将得到OEM代码页编码的文件,这可能是不受欢迎的.

    • 如果可行,您可以从根本上设置控制台窗口以使用您选择的代码页(输入输出编码)(使用chcp),但遗憾的是,尝试在脚本中更改ad-hoc编码不是一种选择.
      请注意,使用UTF-8 - 代码页65001- 仅在将控制台窗口配置为使用TT(TrueType)字体之一时才有效.

  • 如上所述,基于默认输入编码(也是OEM代码页,在PS中反映为;记住:输入将在输出重新编码),遗憾地将正确识别的输入编码集限制为以下内容.:[Console]::InputEncoding

    • ASCII输入(输出上的重新编码将默认保留此编码)
    • 带有 BOM的UTF-16 LE输入(这是PowerShell调用的Unicode,需要重新编码为输出上可能不同的东西)
  • 可以通过添加到调用(默认情况下需要Windows默认代码页编码)对预期的输入编码进行硬编码,但对stdin输入执行相同操作(如图所示)将是非常重要的.-Encoding <enc>Get-Content$Input

    • 例如,使用默认输入编码,如果您明确希望将输入解释为UTF-8(再次注意,在应用输出 [Console]::OutputEncoding编码时):
      powershell -noprofile -command "$Input | % { [text.encoding]::utf8.GetString([Console]::InputEncoding.GetBytes($_)) } | Select-Object -Last %COUNT%"