在Windows批处理中将十六进制值写入文件

8
在Linux中,我们可以进行以下操作:
echo -n -e '\x66\x6f\x6f' > test.txt

如何在Windows批处理中简单地将HEX值写入文件?


你应该在我提供的链接中找到这些信息。据我所知,它不是 Windows XP 的本地功能,因此您需要安装一些资源工具包。但是从 Windows Vista 开始,它应该是本地可用的... - aschipfl
1
如果您想编写仅包含ASCII字符的代码,可以通过set /A命令将十六进制值转换为十进制,并使用这些值从字符串中选择字符;这是一个简单的过程。批处理文件的主要问题是生成0-31范围内的控制字符,虽然这是可以做到的... - Aacini
1
@Aacini 感谢您的帮助。哇,批处理太糟糕了。 - arminb
@arminb - 如果您不需要ascii输出,只需要字符串形式的hex值,是否尝试使用插入符号^转义特殊字符echo ^\x66^\x6f^\x6f >test.txt - Gourav
@arminb - 正如 @Aacini 所提到的,您可以使用 set /Ahex 输出为 Dec - 您应该阅读此解决方案 Hex to Dec Using Set /A ,对于 hex 666f6f 的 dec 值为 6713199。 - Gourav
显示剩余10条评论
3个回答

19
我假设您想要能够在\x00到\xFF范围内写入所有可能的二进制字节。不幸的是,纯批处理没有提供一个简单的机制来实现这一点。
但是有许多选项并不太难。
未记录的!=ExitCodeASCII!动态变量 !=ExitCodeASCII!动态变量报告最近运行的外部命令的返回代码的ASCII值。但它仅限于\x20到\x7E的ASCII码。
我使用延迟扩展,这样我就不必担心毒瘤字符。
返回特定错误代码的最简单机制是使用cmd /c exit N,其中N必须是十进制值。如果要传递十六进制值,则必须先将该值转换为十进制。
Windows批处理使用0xNN表示法来指定十六进制值。如其他人所指出的,您可以使用set /a val=0x66进行转换。另一个选择是使用for /l %%N in (0x66 1 0x66) do ...,其优点是您不需要定义一个中间环境变量来保存该值。
@echo off
setlocal enableDelayedExpansion
set "str="
for %%H in (0x66 0x6f 0x6f) do (
  for /l %%N in (%%H 1 %%H) do cmd /c exit %%N
  set "str=!str!!=ExitCodeASCII!"
)
>test.txt echo(!str!

优点:

  • 纯批处理

缺点:

  • 必须逐个字符地构建字符串
  • 范围限制为0x20-0x7E

FORFILES

FORFILES命令支持0xNN语法,因此它可以生成大多数字符。但字符串必须通过CMD / C传递,因此不能用于生成\0x00、\0x0A或\0x0D。(我没有测试,但我相信所有其他值都可以正常工作,只要有毒字符被适当地引用或转义)

@echo off
forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x660x6f0x6f"

优点:

  • 纯批处理
  • 可以一次性处理整个字符串

缺点:

  • 不支持本机XP
  • 不支持0x00、0x0A或0x0D
  • 毒害字符的转义/引用可能很棘手

CERTUTIL

CERTUTIL支持一个-decodeHex动词,可以读取十六进制值并直接写入文件。

@echo off
>temp.txt echo(66 6f 6f 0d 0a
certutil -f -decodehex temp.txt test.txt >nul
del temp.txt

优点:

  • 纯批处理
  • 支持所有可能的字节码
  • 完全控制换行符和回车符
  • 可以在一次处理中处理整个字符串(或文件!)
  • 速度快

缺点:

  • 需要临时文件

混合JScript /批处理-简单解决方案

在批处理脚本中嵌入和执行JScript非常容易。并且JScript具有本地解释许多转义序列的能力,包括\xNN。但是,\xNN转义序列实际上映射到Unicode代码点,因此一些高阶字节码不能正确映射到字符值。而高阶字节的结果可能会因您计算机的默认字符集而异。

下面我定义了一个:jWrite子例程,它可以写带有嵌入式转义序列的行。如果您想写没有换行符的字符串,则只需在JScript代码中将WriteLine更改为Write

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
@echo off
call :jWrite "\x66\x6f\x6f" >test.txt
exit /b

:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(eval('"'+WScript.Arguments.Unnamed(0)+'"'));

优点:

  • 纯脚本,可在从 XP 开始的所有 Windows 机器上本地运行
  • 非常简单的 JScript 代码
  • 可以一次处理整个字符串
  • 许多其他转义序列可用,例如 \\\t\r\n
  • 易于将普通文本与转义序列混合,但双引号必须为 \x22

缺点:

  • 并非所有高位字节都能得出正确结果
  • 结果依赖于您计算机的默认字符集。如果是 Windows 1252 最好

混合 JScript / 批处理 - 更加稳健,但解决方案较为复杂

编写一些 JScript 代码以正确解释所有 \xNN 代码并给出正确的字节并不太困难,只要您的计算机默认为 Windows-1252。如果您的命令会话的活动代码页也匹配 Windows 1252,则可以自由混合普通文本。

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
@echo off
call :jWrite "\x66\x6f\x6f"
call :jWrite "Hello\nworld\x80"
exit /b

:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(WScript.Arguments.Unnamed(0).replace(
  /\\(\\|b|f|n|r|t|v|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/g,
  function($0,$1) {
    switch ($1.toLowerCase()) {
      case 'x80': return '\u20AC';
      case 'x82': return '\u201A';
      case 'x83': return '\u0192';
      case 'x84': return '\u201E';
      case 'x85': return '\u2026';
      case 'x86': return '\u2020';
      case 'x87': return '\u2021';
      case 'x88': return '\u02C6';
      case 'x89': return '\u2030';
      case 'x8a': return '\u0160';
      case 'x8b': return '\u2039';
      case 'x8c': return '\u0152';
      case 'x8e': return '\u017D';
      case 'x91': return '\u2018';
      case 'x92': return '\u2019';
      case 'x93': return '\u201C';
      case 'x94': return '\u201D';
      case 'x95': return '\u2022';
      case 'x96': return '\u2013';
      case 'x97': return '\u2014';
      case 'x98': return '\u02DC';
      case 'x99': return '\u2122';
      case 'x9a': return '\u0161';
      case 'x9b': return '\u203A';
      case 'x9c': return '\u0153';
      case 'x9d': return '\u009D';
      case 'x9e': return '\u017E';
      case 'x9f': return '\u0178';
      default:    return eval('"'+$0+'"');
    }
  }
));

优点:

  • 纯脚本,在从XP开始的所有Windows机器上本地运行
  • 可以一次性处理整个字符串
  • 支持从\x00到\xFF的所有字节代码
  • 许多其他转义序列可用,例如\\\t\r\n
  • 易于混合普通文本和转义序列,但双引号必须是\x22

缺点:

  • 只有在您的机器默认为Windows-1252时才能对所有字节给出正确结果
  • 中等复杂的JScript代码

JREPL.BAT - 混合JScript / 批处理正则表达式文本处理实用程序

我的JREPL.BAT实用程序最初是设计用于在文本文件上执行正则表达式搜索和替换操作的。但它具有允许轻松编写带有嵌入式转义序列的字符串的选项,并且无论您的机器使用什么默认字符集,它都可以给出所有字节代码的正确结果。

如果您的机器默认为任何单字节字符集,则可以安全地使用以下内容,其中包括从\x00到\xFF的所有可能的转义序列,并且您可以自由混合普通文本和转义序列。

call jrepl $ "\x66\x6f\x6f" /s "=" /x /o test.txt
/s "="选项将未定义的环境变量作为输入源,被解释为一个空字符串。第一个$参数匹配空字符串的末尾。第二个"\x66\x6f\x6f"参数指定替换值。/x选项启用替换字符串中的转义序列,/o test.txt选项指定输出文件。
如果要追加到test.txt,则添加/APP选项。
如果想要\n作为行尾而不是\r\n(Unix风格而不是Windows),则添加/U选项。
如果不想要任何新行终止符,则添加/M选项。
最后,如果您的计算机没有默认的单字节字符集,仍然可以通过指定像Windows-1252这样的单字节字符集来强制所有转义序列的正确结果。但是,如果指定的字符集与命令会话的活动代码页不匹配,则只保证转义序列能正常工作 - 一些普通文本字符可能会给出错误的结果。
call jrepl $ "\x66\x6f\x6f" /s "=" /x /o "test.txt|Windows-1252"

优点:

  • 纯脚本,可在XP及以上版本的任何Windows机器上运行
  • 可一次处理整个字符串
  • 支持从\x00到\xFF的所有字节代码
  • 许多其他转义序列可用,例如\\\t\r\n
  • 易于将普通文本与转义序列混合使用,但双引号必须为\x22\q
  • 默认字符集不受影响-始终可以给出正确结果
  • 一旦您拥有JREPL这样的工具,您会发现它有很多用途。它是一个强大的工具。

缺点:

  • 需要下载第三方脚本

2
@arminb - 我向你挑战,找到一段不具有hacky特性的批处理脚本吧! :-) 这种语言非常有限,这导致了许多创造性的hack来完成批处理中大多数其他脚本语言轻松完成的任务。但这也是批处理的“魅力”之一。尽管存在许多限制,但仍然可以编写出令人惊讶的复杂代码。 - dbenham
1
@dbenham:别再让我们看起来像懒鬼了!;-) - Brent Rittenhouse
如果你知道如何制作一个能够真实表达我内心感受的笑脸,请告诉我!我只需要一个包含1%的童年愤怒,49%的“我理解你,我曾在以前的工作中维护了一个巨大的有用深入内容的维基百科,但总是被人问到‘你知道这个维基吗?’”,以及50%的嫉妒,因为你没有被这种事情玷污或者仍然坚持做它。你真棒。 - Brent Rittenhouse
抱歉,我忘了感谢你向我介绍'forfiles'。看起来它最初是NT的东西,但自Vista以来已经成为标准了?!它非常有用,让我感到困惑的是在此帖子之前我怎么没听说过它。你真棒。 - Brent Rittenhouse
"!=ExitCodeASCII!" 用于编写 ASCII 值。问题是关于将 "HEX 值写入文件"。 - Zimba
显示剩余6条评论

0

将“HEX值写入文件”:

set a=.
for /l %a in (0,1,255) do (
set /a dec=%a >nul
call cmd /c exit /b !dec!
set hex=!=exitcode!
set a=!a! !hex:~-2!
)
echo %a:~2% > file.txt

要从批处理创建一个包含所有值的二进制文件,请参见此处:
https://www.dostips.com/forum/viewtopic.php?f=3&t=5326 certutil可以同时实现以下两个功能:从二进制文件创建十六进制文件,以及从十六进制编码的文件创建二进制文件。
certutil -f -decodeHex hex.txt out.bin >nul
certutil -f -encodeHex out.bin out.txt >nul

从CMD编写具有给定(解码)十六进制值字节的二进制文件:

set data="66 6F 6F"
echo Dim File : Set File = CreateObject("Scripting.FileSystemObject").CreateTextFile("test.txt") > tmp.vbs
echo data = %data% >> tmp.vbs
echo data = Split (data) >> tmp.vbs
echo for each x in data >> tmp.vbs
echo File.write chr("&H" ^& x) >> tmp.vbs
echo next >> tmp.vbs
echo File.Close >> tmp.vbs
cscript //nologo tmp.vbs

如果你只需要写可打印字符(从0x20到0x7E),那么!=ExitCodeASCII!是一个不错/更快的(批处理)解决方案。

0
除了 @dbenham 的出色和全面的答案之外,使用 PowerShell Core 6.x+(在所有支持的 Windows 系统、MacOS 和 Linux 上都可用,位于 http://github.com/PowerShell/Powershell)是另一种通过 运行 的方法。
pwsh.exe -NoLogo -NoProfile -Command Write-Host "`u{64}`u{65}`u{66}" >test.txt

请注意,此方法适用于PowerShell Core。截至目前,我尚未找到一种方法使其与Windows PowerShell 5.x兼容,该版本是作为Microsoft Windows系统的一部分最新的版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接