这是我想要做的:
正确地重定向控制台应用程序的输出(我使用术语命令在消息的其余部分)放入具有 1252 编码的文件中(以使其在默认配置下可以从任何记事本软件读取。
我观察到的情况:
Chcp 对内部命令和一些外部命令(最近的命令)有效
首先值得注意的是 CHCP 在 Win7 和 Win 10 下的运行方式不同。
如果从 cmd 提示符运行以下批处理,您会注意到命令输出在 win10 控制台中正确显示,而 win7 控制台会严重呈现 ASCII 字符。
for /f "tokens=2 delims=:" %%G in ('chcp') do Set _cp_=%%G
chcp 1252
@echo test an internal command
dir
@echo test an external (recent) command: Robocopy
robocopy .\ .\ /L
@echo test an external (legacy) command: Xcopy
xcopy test.txt 2>&1
chcp %_cp_%
echo end of test.cmd batch
顺便说一句,我感兴趣的是知道是什么导致了这种差异,尽管这并不是该消息的真正目的,并且可以通过在第一个 chcp 命令后的批处理中添加 ps 调用“powershell [console]::outputencoding=[system.text.encoding]::getencoding(850)”轻松修复它。
无论批处理输出重定向到文件时发生什么实际问题:test.cmd > test.txt。
在这种情况下,无论操作系统是什么,结果都是相同的。内部命令和新的外部命令(Robocopy、Bcdedit 等)的输出都经过了正确的 1252 编码。旧命令(xcopy、chcp 等)则不是(以 OEM 代码页输出)。简而言之,大多数命令不受 CHCP 或通过 powershell 进行的等效 [控制台] 更改的影响。
关于这一混乱局面有各种猜测:
旧命令代码基于 CRT,而内部命令和最新的外部命令使用 Win32 API。它基于上一节关于控制台应用程序开发的内容,来自MSDN 全球化分步指南!
由于至少在 win10 中,控制台中显示的内容(所有命令输出的编码相同)和存储的文件不同(输出编码根据命令而变化),因此输出/输入流可能根据它们指向的句柄类型以不同的方式处理。在重定向的情况下,控制台函数可用于显示和 I/O 文件函数。基于以下推测高级控制台输入和输出函数!
MS 建议控制台应用程序的代码强制对输出流进行 OEM 编码。参考控制台应用程序问题 如果在外部命令的代码中应用了 MS 建议,这也许可以解释为什么无论应用了哪种控制台代码页,它们的输出流重定向到文件时始终编码为 OEM_CP。奇怪的是,readfile 和 writefile 并未被提及在受设置文件API到OEM
最后,我不知道旧命令和最近引入的命令之间的区别是否是因为它们的代码遵循 MS 的建议,而仅仅因为字符串文字是按照 OEM 而不是 ANSI 编码的。
可能的解决方案/解决方法
如果3是正确的,它们肯定很少。:可以更改注册表项 HKLM\system\currentset\control\NLS\codepage OEMCP=1252 的值。这不安全(不要尝试设置 Unicode 65001,您的系统可能会拒绝启动)并且不方便(需要重新启动)。或者,只用 OEM 编码内容填充文件,并在批处理结束时使用 PS 脚本对文件进行转码。如果必须定期访问和检查文件,则这种方法简单但不太优雅。
如果2是正确的,可能存在控制 I/O 文件函数 readfile 和 writefile 编码的函数。
如果1是正确的,应该可以控制当前用户会话的国际设置或文化,从而控制 CRT 应用程序的代码页。从 Win8 开始,可以通过Powershell 在 Windows 中配置国际设置. 命令行应用程序是也能做这样的事情无论如何,这里的困难在于如何创建一种“文化”,将 OEM 代码页设置为 1252,预定义集合中不存在的。
即使没有针对该问题的有效解决方案,也请毫不犹豫地分享您对该主题的知识。我只是好奇想知道 MS 是如何实现这些东西的。
答案1
4 年后我才意识到 :-p 我从未发布过我在脚本中经常使用的解决这个问题的解决方法。这是一个短批处理,有问题的命令(即几乎所有的外部批处理命令)都通过管道传输。
:: Description : Transcode the OEM output stream of external commands to ANSI chars in order to get notepad readable files with redirections
:: Usage: prog.exe | output1252
:: 2>&1 prog.exe | output1252 > log.txt
@echo off
setlocal
:: CodePage of the command and console programs (OEM code page)
set OEM_CP=850
chcp %OEM_CP% >NUL
:: The default local Windows codepage (ANSI code page), for Western Europe: 1252
set ANSI_CP=1252
>NUL chcp %ANSI_CP% & for /F delims^=^ eol^= %%A in ('more') do (
call :WRITEOUT %%A)
echo:
goto :eof
:WRITEOUT
echo %*
goto :eof
- 删除空行(通过 FOR /F 指令):在脚本的最后插入一行,使日志文件更具可读性
- 由于“更多”指令功能,无法处理二进制数据:TAB 代码转换为空格等
- 将 STDErr 重定向到 STDout 以进行转码
另一个可以接受管道命令或重定向文件的
:: Description : Transcode OEM 850 input data to ANSI-1252 output
:: Usage: ThatBatch <850.txt >1252.txt
:: prog.exe | ThatBatch
:: 2>&1 prog.exe | ThatBatch > log.txt
@echo off
0<NUL chcp 850 >NUL
clip
0<NUL chcp 1252 >NUL
powershell Get-Clipboard
- 数据流不受影响(甚至可以对二进制数据使用原始选项)
- 对于短内容来说,速度比前一个稍微慢一些,但对于大块数据或大文件来说,速度可能会更快。
- 需要 PowerShell 5
这些示例将 OEM 850 转码为 ANSI-1252,但它们可以轻松适应转码任何代码页(可能还有 Unicode)。请随意改进它们(将输入/输出代码页作为参数实现)并满足您的需求。