将stdin放入Powershell流中 [英] Getting stdin into the Powershell stream

查看:89
本文介绍了将stdin放入Powershell流中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在命令行上指定文件名时,以下脚本可以很好地发挥作用.

尾巴

@echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
powershell "Get-Content %FILENAME% -Last %COUNT%"

但是,我需要的是能够将文本从stdin传递到Get-Content中.我想编写以下代码,以获取分配给该项目的最后三个Subversion标记.我该怎么做才能将源文件设为Get-Content成为标准输入?

svn ls svn://ahost/arepo/aproject/tags | call tail.bat -3

注意:我不允许从外部安装任何有用的工具,例如tail.必须使用机器上已经可用的程序来完成.

更新:

@ mklement0提供了答案.由此,我添加了代码以使用默认的COUNT值10(如果未提供).这与UNIX/Linux方式相符.

@echo off

SET "COUNT=%~1"
IF "%COUNT:~0,1%" == "-" (
    SET "COUNT=%COUNT:~1%"
    SHIFT
) ELSE (
    SET "COUNT=10"
)
SET "FILENAME=%~1"

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

EXIT /B

解决方案

重写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%"
)

这将使PowerShell通过$Input读取stdin输入,如果未传递文件名参数,则由此答案提供

示例:

C:> (echo one & echo two & echo three) | tail.bat -2
two
three

注意:

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

  • 因此,鉴于 $Input 是表示 outside stdin输入的枚举器,我们可以确定它逐一枚举输入的文本行(作为字符串),因此我们只需要选择感兴趣的行,这就是为什么对Select-Object进行管道传递就足够了./p>

  • 相比之下,在PowerShell中按名称读取文件需要Get-Content(顺便说一句,它还会通过管道逐个发送输入文件的行,除非您还指定-Raw);由于Get-Content具有内置的tail功能,因此可以通过参数-Tail(及其别名-Last)来完成此操作.

  • CAVEAT : PowerShell与外界对话时涉及输入字符解码和输出重新编码:

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

    • 否则,请准备一个痛苦的世界-有关详细信息,请参见下文.


解码/重新编码问题

  • 假定PowerShell可以识别您的 input 编码(请参见下文),则 output 编码总是 控制台窗口的分配编码为;不幸的是,默认情况下,默认情况下,这就是OEM代码页(例如,在美式英语系统上的"DOS"代码页CP437),在PS中显示为[Console]::OutputEncoding.

    • 因此,在正确识别输入的情况下,如果打印到控制台,则一切正常,但是,如果将输出捕获到文件中,则表示最终将得到OEM代码页编码的文件,这可能是不希望的.

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

  • 如上所述,不幸的是,基于默认输入编码(也是PS中反映的OEM代码页),可以正确识别的 input 编码集仅限于以下几种为[Console]::InputEncoding;请记住:输入将在输出上进行重新编码):

    • ASCII 输入(默认情况下,对输出重新编码将保留此编码)
    • 具有BOM表的 UTF-16 LE输入 (PowerShell称为Unicode,需要重新编码为输出上可能有所不同的内容)
  • 可以通过在Get-Content调用中添加-Encoding <enc>来硬编码预期的 input 编码(默认情况下,预期Windows默认代码页编码) ),但对 stdin 输入(如$Input所示)进行相同操作将是不平凡的.

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

The following script works well when the filename is specified on the command line.

tail.bat

@echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
powershell "Get-Content %FILENAME% -Last %COUNT%"

However, what I need is to be able to pipe the text into Get-Content from stdin. I would like to write the following to get the last three Subversion tags assigned to the project. What can I do to get the source to Get-Content to be stdin?

svn ls svn://ahost/arepo/aproject/tags | call tail.bat -3

NB: I am not permitted to install any helpful tools like tail from the outside. It has to be done with the programs already available on the machine.

Update:

@mklement0 provided the answer. From that, I added code to use a default COUNT value of 10 if it is not provided. This matches the UNIX/Linux way.

@echo off

SET "COUNT=%~1"
IF "%COUNT:~0,1%" == "-" (
    SET "COUNT=%COUNT:~1%"
    SHIFT
) ELSE (
    SET "COUNT=10"
)
SET "FILENAME=%~1"

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

EXIT /B

解决方案

Rewrite tail.bat as follows:

@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%"
)

This will make PowerShell read stdin input via $Input, if no filename argument was passed, courtesy of this answer.

Example:

C:> (echo one & echo two & echo three) | tail.bat -2
two
three

Note:

  • While PowerShell generally sends through the pipeline and outputs objects of any kind, its interface to the outside world invariably involves strings.

  • Thus, given that $Input is an enumerator that represents outside stdin input, we can be sure that it enumerates the input text lines (as strings) one by one, so all we need is to select the lines of interest, which is why piping to Select-Object is sufficient.

  • By contrast, reading a file by name in PowerShell requires Get-Content (which, incidentally, also sends the input file's lines one by one through the pipeline, unless you also specify -Raw); since Get-Content has tail functionality built in, via parameter -Tail (and its alias -Last), it is all that is needed here.

  • CAVEAT: Character decoding on input and re-encoding on output is involved when PowerShell talks to the outside world:

    • If you're only ever dealing with ASCII-encoded input (single-byte characters with code points ranging between 0 - 127), you needn't worry.

    • Otherwise, prepare for a world of pain - see below for details.


Decoding/Re-Encoding Issues

  • Assuming that PowerShell recognizes your input encoding (see below), the output encoding is invariably what the console window's assigned encoding is; by default, unfortunately, that is the OEM codepage (e.g., the "DOS" code page CP437 on US-English systems), reflected in PS as [Console]::OutputEncoding.

    • Thus, with properly recognized input, if you print to the console, things will look OK, but if you capture the output in a file, you'll end up with an OEM-codepage-encoded file, which is probably undesired.

    • If feasible, you could fundamentally set up your console windows to use your codepage (input and output encoding) of choice (using chcp), but trying to change the encoding ad-hoc in your script is, unfortunately, not an option.
      Note that using UTF-8 - codepage 65001 - only works if you configure your console windows to use one of the TT (TrueType) fonts.

  • As written above, the set of input encodings that are properly recognized is unfortunately limited to the following, based on the default input encoding (which is also the OEM codepage, reflected in PS as [Console]::InputEncoding; remember: input will be re-encoded on output):

    • ASCII input (re-encoding on output will by default preserve this encoding)
    • UTF-16 LE input with a BOM (which is what PowerShell calls Unicode, subject to re-encoding to something potentially different on output)
  • You could hard-code an expected input encoding by adding -Encoding <enc> to the Get-Content call (which expects the Windows default codepage encoding by default), but to do the same for stdin input (as reflected in $Input) would be non-trivial.

    • E.g., with the default input encoding, if you explicitly wanted to interpret the input as UTF-8 (again, note that on output [Console]::OutputEncoding encoding is applied):
      powershell -noprofile -command "$Input | % { [text.encoding]::utf8.GetString([Console]::InputEncoding.GetBytes($_)) } | Select-Object -Last %COUNT%"

这篇关于将stdin放入Powershell流中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆