powershell - “SFC”输出重定向格式问题 - Powershell / Batch
问题描述
我正在研究一个 powershell 脚本,其中几个命令输出显示在窗口中并附加到文件或变量中。在我使用该sfc
命令之前它工作正常。当管道或重定向时,输出是“损坏的”:
> sfc /?
Vérificateur de ressources Microsoft (R) Windows (R) version 6.0[...]
> sfc /? | Tee-Object -Variable content
V Ú r i f i c a t e u r d e r e s s o u r c e s M i c r o s o f t ( R ) W i n d o w s ( R ) v e r s i o á 6 . 0[...]
是否有其他类似的命令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
解决方案
如js2010 的回答中所述,该sfc.exe
实用程序 - 令人惊讶的是 -输出UTF-16 LE(“Unicode”)编码的文本。
由于 PowerShell 没有预料到,它会误解sfc
的输出。[1]
解决方案是(暂时)更改[console]::OutputEncoding
为 UTF-16LE,它告诉 PowerShell / .NET 期望从外部程序获得什么字符编码,即如何将外部程序输出解码为.NET 字符串(存储为 UTF-16 代码内存中的单位)。
但是,还有一个看起来像错误的附加问题:奇怪的是,sfc.exe
它使用 CRCRLF( `r`r`n
) 序列作为换行符,而不是 Windows 习惯的 CRLF( `r`n
) 换行符。
PowerShell,当它从外部程序捕获标准输出输出时,返回一个行数组而不是单个多行字符串,并且它可以互换地处理以下换行样式:CRLF(Windows 样式)、LF(Unix 样式)和 CR (过时的 Mac 风格 - 现在非常罕见)。
因此,它将 CRCRLF 视为两个换行符,它们反映在“teed”和变量中捕获的输出中,然后包含额外的空行。
因此,解决方案是将数组元素与标准 CRLF 换行符序列连接起来 -(sfc /?) -join "`r`n"
然后将2 个连续 `r`n
替换为1个,以删除人为引入的换行符:-replace "`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
's 的情况,让我觉得不寻常(其他程序可能会在选择加入的基础上提供)。
具有仅限 Windows 传统的旧控制台程序使用(可能已修复)OEM 代码页,这是一种单字节 8 位编码,是 ASCII 的超集。
现代的多平台控制台程序越来越多地使用 UTF-8(例如 Node.js CLI),它是一种可变宽度编码,能够编码与 ASCII 向后兼容的所有 Unicode 字符(即,在 7-位 ASCII 范围 UTF-8 将所有字符编码为单个 ASCII 兼容字节)。
如果您想让您的 PowerShell 会话和可能的所有控制台窗口完全支持 UTF-8,请参阅此答案(但是,这样做仍然需要上述解决方法sfc
)。
[1] 直接到控制台输出:
当sfc
输出既不被 PowerShell 捕获也不通过 cmdlet 路由时Tee-Object
,直接sfc
写入控制台,大概使用 Unicode 版本的WriteConsole
Windows API 函数,它需要 UTF-16LE 字符串。
以这种方式写入控制台允许打印所有 Unicode 字符,而与当前处于活动状态的代码页(反映在chcp
/中)无关。[console]::OutputEncoding
(虽然某些字符的渲染可能会不足,但由于有限的字体支持和缺乏对 BMP(基本多语言平面)之外的(稀有)字符的支持,控制台缓冲区正确地保留了所有字符,因此在别处复制和粘贴可能会渲染正确存在-请参阅此答案的底部。)
因此,直接到控制台的输出不受误解的影响,并且通常按预期打印。
推荐阅读
- angular - 如何使用子网作为角度路由路径
- python - 从变量而不是文件在python opencv中打开下载的图像
- symfony - 如何防止字段带有链接
- amazon-web-services - Elastic Beanstalk 将 http 重定向到 https 用于 iis 站点
- android - 左右滑动的Android可扩展列表视图
- python - 按标题在 Outlook 中查找特定邮件
- google-cloud-platform - 使用 Deployment Manager 进行部署时,上传本地文件以使其在构建步骤中可访问
- python - DJango 中的模型类型
- neo4j - Neo4j 社区/企业/桌面是否可以在没有互联网的情况下工作
- react-native - 如何获取放置在自定义标记或标注视图内的多个可触摸组件的 onPress 事件?