&引用;证监会;输出重定向格式化问题-Powershell/批处理

&引用;证监会;输出重定向格式化问题-Powershell/批处理,powershell,batch-file,encoding,formatting,io-redirection,Powershell,Batch File,Encoding,Formatting,Io Redirection,我正在编写一个powershell脚本,其中几个命令输出显示在窗口中,并附加到文件或变量中。在我使用sfc命令之前,它工作正常。管道或重定向时,输出为“断开”: 是否有其他类似于sfc的命令以相同的方式格式化,或者如果重定向会导致输出中断 编辑 Powershell示例代码,使用已接受答案中的代码: # Run a command function RunCommand([ScriptBlock] $command) { # Run the command and write the

我正在编写一个powershell脚本,其中几个命令输出显示在窗口中,并附加到文件或变量中。在我使用
sfc
命令之前,它工作正常。管道或重定向时,输出为“断开”:

是否有其他类似于
sfc
的命令以相同的方式格式化,或者如果重定向会导致输出中断


编辑

Powershell
示例代码,使用已接受答案中的代码:

# Run a command
function RunCommand([ScriptBlock] $command) {

    # Run the command and write the output to the window and to a variable ("SFC" formatting)
    $stringcommand = $command.ToString()
    if (
        $stringcommand -match "^SFC$" -or
        $stringcommand -match "^SFC.exe$" -or
        $stringcommand -match "^SFC .*$" -or
        $stringcommand -match "^SFC.exe .*$"
    ) {
        $oldEncoding = [console]::OutputEncoding
        [console]::OutputEncoding = [Text.Encoding]::Unicode
        $command = [ScriptBlock]::Create("(" + $stringcommand + ")" + " -join ""`r`n"" -replace ""`r`n`r`n"", ""`r`n""")
        & ($command) 2>&1 | Tee-Object -Variable out_content
        [console]::OutputEncoding = $oldEncoding

    # Run the command and write the output to the window and to a variable (normal formatting)
    } else {
        & ($command) 2>&1 | Tee-Object -Variable out_content
    }

    # Manipulate output variable, write it to a file...
    # ...
    return
}

# Run commands
RunCommand {ping 127.0.0.1}
RunCommand {sfc /?}
[void][System.Console]::ReadKey($true)
exit

CMD
示例代码,使用
more
格式化
sfc
输出:

@echo off
setlocal enabledelayedexpansion
set "tmpfile=%TEMP%\temp.txt"
set "outputfile=%TEMP%\output.txt"

REM; Run commands
call :RunCommand "ping 127.0.0.1"
call :RunCommand "sfc"
pause
exit /b

REM; Run a command
:RunCommand

    REM; Run the command and write the output to the window and to the temp file
    set "command=%~1"
    (!command! 2>&1) >!tmpfile!

    REM; Write the output to the window and to the output file ("SFC" formatting)
    set "isSFC=0"
    (echo !command!|findstr /I /R /C:"^SFC$" > NUL) && (set "isSFC=1")
    (echo !command!|findstr /I /R /C:"^SFC.exe$" > NUL) && (set "isSFC=1")
    (echo !command!|findstr /I /R /C:"^SFC .*$" > NUL) && (set "isSFC=1")
    (echo !command!|findstr /I /R /C:"^SFC.exe .*$" > NUL) && (set "isSFC=1")
    (if !isSFC! equ 1 (
        (set \n=^
%=newline=%
)
        set "content="
        (for /f "usebackq tokens=* delims=" %%a in (`more /p ^<"!tmpfile!"`) do (
            set "line=%%a"
            set "content=!content!!line!!\n!"
        ))
        echo.!content!
        (echo.!content!) >>!outputfile!

    REM; Write the output to the window and to the locked output file (normal formatting)
    ) else (
        type "!tmpfile!"
        (type "!tmpfile!") >>!outputfile!
    ))
goto :EOF
@echo关闭
延迟扩展
设置“tmpfile=%TEMP%\TEMP.txt”
设置“outputfile=%TEMP%\output.txt”
REM;运行命令
调用:RunCommand“ping 127.0.0.1”
调用:RunCommand“sfc”
暂停
退出/b
REM;运行命令
:RunCommand
REM;运行命令并将输出写入窗口和临时文件
设置“命令=%~1”
(!command!2>&1)>!tmpfile!
REM;将输出写入窗口和输出文件(“SFC”格式)
设置“isSFC=0”
(echo!command!| findstr/I/R/C:“^SFC$”>NUL)和&(set“isSFC=1”)
(echo!command!| findstr/I/R/C:“^SFC.exe$”>NUL)和&(设置“isSFC=1”)
(echo!command!| findstr/I/R/C:“^SFC.*$”>NUL)和&(设置“isSFC=1”)
(echo!command!| findstr/I/R/C:“^SFC.exe.*$”>NUL)和&(设置“isSFC=1”)
(如果!isSFC!相等于1(
(设置\n=^
%=换行符=%
)
设置“内容=”
(对于/f“usebackq令牌=*delims=“%%a in(`more/p^>!outputfile!
REM;将输出写入窗口和锁定的输出文件(正常格式)
)否则(
键入“!tmpfile!”
(键入“!tmpfile!”)>>!outputfile!
))
后藤:EOF

看起来sfc输出的是unicode,没有bom。太棒了

cmd /c 'sfc > out'
get-content out -Encoding Unicode | where { $_ } # singlespace
输出:

Microsoft (R) Windows (R) Resource Checker Version 6.0
Copyright (C) Microsoft Corporation. All rights reserved.
Scans the integrity of all protected system files and replaces incorrect versions with
correct Microsoft versions.
SFC [/SCANNOW] [/VERIFYONLY] [/SCANFILE=<file>] [/VERIFYFILE=<file>]
    [/OFFWINDIR=<offline windows directory> /OFFBOOTDIR=<offline boot directory>]
/SCANNOW        Scans integrity of all protected system files and repairs files with
                problems when possible.
/VERIFYONLY     Scans integrity of all protected system files. No repair operation is
                performed.
/SCANFILE       Scans integrity of the referenced file, repairs file if problems are
                identified. Specify full path <file>
/VERIFYFILE     Verifies the integrity of the file with full path <file>.  No repair
                operation is performed.
/OFFBOOTDIR     For offline repair specify the location of the offline boot directory
/OFFWINDIR      For offline repair specify the location of the offline windows directory
e.g.
        sfc /SCANNOW
        sfc /VERIFYFILE=c:\windows\system32\kernel32.dll
        sfc /SCANFILE=d:\windows\system32\kernel32.dll /OFFBOOTDIR=d:\ /OFFWINDIR=d:\windows
        sfc /VERIFYONLY

看起来sfc输出的是unicode,没有bom。太棒了

cmd /c 'sfc > out'
get-content out -Encoding Unicode | where { $_ } # singlespace
输出:

Microsoft (R) Windows (R) Resource Checker Version 6.0
Copyright (C) Microsoft Corporation. All rights reserved.
Scans the integrity of all protected system files and replaces incorrect versions with
correct Microsoft versions.
SFC [/SCANNOW] [/VERIFYONLY] [/SCANFILE=<file>] [/VERIFYFILE=<file>]
    [/OFFWINDIR=<offline windows directory> /OFFBOOTDIR=<offline boot directory>]
/SCANNOW        Scans integrity of all protected system files and repairs files with
                problems when possible.
/VERIFYONLY     Scans integrity of all protected system files. No repair operation is
                performed.
/SCANFILE       Scans integrity of the referenced file, repairs file if problems are
                identified. Specify full path <file>
/VERIFYFILE     Verifies the integrity of the file with full path <file>.  No repair
                operation is performed.
/OFFBOOTDIR     For offline repair specify the location of the offline boot directory
/OFFWINDIR      For offline repair specify the location of the offline windows directory
e.g.
        sfc /SCANNOW
        sfc /VERIFYFILE=c:\windows\system32\kernel32.dll
        sfc /SCANFILE=d:\windows\system32\kernel32.dll /OFFBOOTDIR=d:\ /OFFWINDIR=d:\windows
        sfc /VERIFYONLY
如中所述,
sfc.exe
实用程序输出的文本是LE(“Unicode”)编码的

由于PowerShell没有预料到这一点,因此它误解了sfc的输出。[1]

解决方案是(暂时)将
[控制台]::outputeneCoding
更改为UTF-16LE
,它告诉PowerShell/.NET外部程序需要什么字符编码,即如何将外部程序输出解码为.NET字符串(作为UTF-16代码单元存储在内存中)

然而,还有一个看起来像bug的问题:奇怪的是,
sfc.exe使用CRCRLF(
`r`n
)序列作为换行符,而不是Windows常用的CRLF(
`r`n
)换行符

PowerShell从外部程序捕获stdout输出时,会返回一个行数组,而不是一个多行字符串,并且它可以互换地处理以下换行样式:CRLF(Windows样式)、LF(Unix样式)和CR(过时的Mac样式-现在非常罕见)。
因此,它将CRLF视为两个换行符,这两个换行符反映在“T”和捕获的变量输出中,然后包含额外的空行。
因此,解决方案是使用标准的CRLF换行序列连接数组元素-
(sfc/?)-连接“`r`n”
,然后仅用一个替换两个连续的
`r`n
,以移除人为引入的换行符:
-替换“`r`n`r`n”,“`r`n”

总而言之:

# Save the current output encoding and switch to UTF-16LE
$prev = [console]::OutputEncoding
[console]::OutputEncoding = [Text.Encoding]::Unicode

# Invoke sfc.exe, whose output is now correctly interpreted and
# apply the CRCRLF workaround.
# You can also send output to a file, but note that Windows PowerShell's 
# > redirection again uses UTF-16LE encoding.
# Best to use ... | Set-Content/Add-Content -Encoding ... 
(sfc /?) -join "`r`n" -replace "`r`n`r`n", "`r`n" | Tee-Object -Variable content

# Restore the previous output encoding, which is the system's 
# active OEM code page, which should work for other programs such
# as ping.exe
[console]::OutputEncoding = $prev
请注意,
$content
将包含一个多行字符串;使用
$content-split“`r`n”
将其拆分为一个行数组


至于:

是否有其他类似“sfc”的命令以相同的方式格式化,或者如果重定向会导致输出中断

并非我个人所知;无条件UTF-16LE输出,如
sfc.exe
的情况,让我感到不同寻常(其他程序可能会在选择加入的基础上提供)

旧的控制台程序使用(可能是固定的)OEM代码页,这是一种单字节8位编码,是ASCII的超集

现代多平台控制台程序越来越多地使用UTF-8(例如Node.js CLI),这是一种可变宽度编码,能够对所有与ASCII向后兼容的Unicode字符进行编码(即,在7位ASCII范围内,UTF-8将所有字符编码为单个ASCII兼容字节)

如果要使PowerShell会话和可能的所有控制台窗口完全了解UTF-8,请参阅(但是,要做到这一点,仍然需要针对
sfc
的上述解决方法)


[1] 直接到控制台输出

当PowerShell既不捕获
sfc
输出,也不通过诸如
Tee Object
之类的cmdlet路由时,
sfc
直接写入控制台,可能使用Unicode版本的Windows API函数,该函数需要UTF-16LE字符串

以这种方式写入控制台允许打印所有Unicode字符,而不考虑当前处于活动状态的代码页(反映在
chcp
/
[控制台]::outputeneCoding
)。 (虽然某些字符的呈现可能会有缺陷,但由于字体支持有限,并且缺少对BMP(基本多语言平面)之外的(罕见)字符的支持,控制台缓冲区会正确保留所有字符,因此在其他位置复制和粘贴可能会在此处正确呈现-请参阅本部分的底部。)

因此,直接到控制台输出不受误解的影响,通常按预期打印。

如中所述,
sfc.exe
实用程序-出乎意料地-输出的文本是LE(“Unicode”)编码的文本

由于PowerShell没有预料到这一点,因此它误解了sfc的输出。[1]

解决方案是(临时)将
[控制台]:OutputEncoding
更改为UTF-16LE
,这会告诉Powe