【问题标题】:Broken Cmd scripts after creation with PowerShell Write-Output使用 PowerShell 写入输出创建后损坏的 Cmd 脚本
【发布时间】:2020-11-17 16:07:43
【问题描述】:

我们有一个域范围的自动化工具,可以以管理员身份在服务器上启动作业(Stonebranch UAC - 请注意:这与 Windows“用户访问控制”无关,Stronebranch UAC 是一个企业自动化工具)。它在本地寻找 Cmd 批处理脚本,所以我们使用它们。

但是,我更喜欢使用 PowerShell 来处理所有事情,因此我使用 PowerShell 批量创建了数十个 .bat 脚本。没有任何效果,每当它尝试运行 .bat 脚本时,自动化工具就会崩溃。所以我削减了脚本,使它们包含一行echo 123,但一切都被破坏了。我们认为这是该工具的问题,但随后尝试在服务器上运行 .bat 脚本,它们也被破坏了,只是在命令行上生成了一些 unicode 并且无法运行。

因此,我们突然意识到 PowerShell 如何使用Write-Output 命令创建批处理脚本正在破坏它们(这是在 Windows 2012 R2 上,而 PowerShell 是 5.1)。我重复一遍,例如,如果我在 PowerShell 控制台上键入以下内容:

Write-Output "echo 123" > test.bat

如果我现在打开 cmd.exe 然后尝试运行 test.bat,我只会在屏幕上看到 2 个类似 unicode 的字符,而没有其他任何内容。

有人可以向我解释 a) 为什么会发生这种行为,以及 b) 我如何才能继续使用 PowerShell 来生成这些批处理脚本 它们不会被破坏?即我是否必须更改 BOM 或 UTF-8 设置或其他任何东西才能使其正常工作,请问我该怎么做?

【问题讨论】:

  • 不是 PowerShell 专家,但据我所知,PowerShell 默认写入 Unicode。有一个写ANSI/ASCII的开关,但是我不记得了……
  • 正如斯蒂芬指出的那样,您需要处理文件的编码。不要使用重定向的东西......而是使用允许您显式设置编码的“to file”命令之一。看看你通过搜索powershell set file encoding 得到什么... [grin]
  • 所以您认为带有编码标志的Set-Content 是一种方式(我永远无法理解所有的Unicode、UTF8 和BOM 以及所有这些东西!)?嗯,或者任何人都有从 PowerShell 创建批处理文件的 最佳 方法的示例(甚至,我如何获取文件并使用一些特殊的 cmdlet 来transmogrify 将该文件转换为非 Unicode 文件?)docs.microsoft.com/en-us/powershell/module/…

标签: powershell batch-file utf-8 automation windows-server-2012-r2


【解决方案1】:

Windows PowerShell 中,> 与底层的 Out-File cmdlet 一样,总是[1]创建“Unicode " (UTF-16LE) 文件,cmd.exe 无法读取(即使使用 /U 开关也不行)。

PowerShell [Core] v6+ 中,始终使用无 BOM 的 UTF-8 编码,包括 >

因此:

  • 如果您使用 PowerShell [Core] v6+ 并且批处理文件的内容仅包含 ASCII 范围字符(7 位范围),您可以获得离开>

  • 否则,使用Set-Content-Encoding Oem

'@echo 123' | Set-Content -Encoding Oem test.bat

如果您的批处理文件源代码只包含 ASCII 范围字符(7 位范围),您也可以侥幸逃脱(在两个 PowerShell 版本中):

'@echo 123' | Set-Content test.bat

注意:

  • 正如-Encoding 参数所暗示的那样,使用系统的活动OEM 代码页,这是批处理文件所期望的。

  • OEM 代码页是 ASCII 编码的超集,因此使用-Encoding Oem 保存且 由 ASCII 范围字符组成的文件隐含地也是一个 ASCII 文件。这同样适用于仅由 ASCII 范围字符组成的 BOM-less UTF-8 和 ANSI (Default) 编码文件。

  • -Encoding Oem - 与-Encoding Ascii 相对,甚至使用Set-Content默认 编码[2] - 因此只有在您使用非ASCII 时才重要- 批处理文件源代码中的字符范围,例如é。但是,鉴于 OEM 代码页是固定宽度的单字节编码,因此此类字符总共限制为一组 256 个字符,这意味着许多 Unicode 字符本质上是不可用的,例如


[1] 在 Windows PowerShell v5.1(及更高版本)中,可以通过 $PSDefaultParameterValues 首选项变量更改 > 的编码 - 请参阅 this answer - 但是,您不会能够选择 BOM-less UTF-8 编码,这是创建批处理文件(仅由 ASCII 范围字符组成)所必需的。

[2] Set-Content 的默认编码是 Windows PowerShell(另一个 ASCII 超集)中的活动 ANSI 代码页 (Default),并且(对于所有 cmdlet) PowerShell [Core] v6+ 中的无 BOM UTF-8;有关 Windows PowerShell 中极为不一致的字符编码的概述,请参阅this answer

【讨论】:

  • 是否有一个 cmdlet 可以查看文件并仅返回编码信息? (我想可能是 Get-Content <file> -Encoding 但这会产生错误。另外,是否有一个 cmdlet 可以获取一个文件,读取它,然后删除该文件并将其替换为具有相同内容但编码不同的文件?
  • @YorSubs:没有内置任何东西,但PowerShellCookbook module 有一个Get-FileEncoding cmdlet 可以将类似的命令直接添加到PowerShell)。同样,没有内置的转码 cmdlet,但您可以使用 .NET 来实现;例如,参见this answer
  • 请注意,有一个longstanding feature request 可以将Get-FileEncoding 之类的东西直接带到PowerShell,并且在相关的RFC discussion 中简要讨论了转码cmdlet (Convert-FileEncoding),但没有人加强进一步追求这一点。
  • 感谢您提供的所有信息,真的很有趣。真遗憾他们没有包含Get-FileEncoding cmdlet,它只是提供信息,所以不能破坏任何东西并且非常有用(即在if ($(Get-FileEncoding file) -eq "OEM") { do a thing } 上进行测试。Linux 一直有类似dos2unix 的东西,所以再一次,很遗憾他们不会实现像Convert-FileEncording 这样的东西,因为编码实际上是一场噩梦,因为它是不可见且难以理解的(它只会在我们最不期望的时候咬我们,并在文件看起来时引起混乱好的,否则)。
  • 是的,这会很有帮助。请注意,dos2unix 只转换 行尾,而不是字符编码,但类 Unix 平台有 file 实用程序,它也报告编码,iconv 实用程序用于转码。如果您安装了 WSL,您至少可以从那里使用它们(您可以针对 Windows 文件系统)。
【解决方案2】:

一种默认创建无 bom utf8 文件的方法是:

    new-item -Path outfile.bat -itemtype file -value "echo this"

【讨论】:

  • 确实(Windows PowerShell 中的另一个不一致),但这仅适用于批处理文件内容仅限于 ASCII 范围字符(7 位代码点),在这种情况下,您可以简单地使用'echo this' | Set-Content outfile.bat。如果存在 8 位范围内的字符,则必须使用 'echo this' | Set-Content -Enocding oem outfile.bat
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-10
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 2016-03-01
相关资源
最近更新 更多