windows - Strawberry Perl——默认情况下编码转换在哪里完成?

标签 windows powershell perl unicode

基本上,我编写了一个 Perl 脚本,为 Powershell 创建一个编码命令并尝试运行它。在对它进行 base64 编码之前,我必须将命令字符串显式转换为 utf-16。我想知道为什么这就是我必须让脚本运行的全部。在运行与控制台或文件系统交互的“普通”程序时,Windows* 上的 Perl 默认执行哪些转换?例如,argv 是否被转换?标准输入/标准输出是否转换?文件IO是否经过转换?

✱ 特别是 Strawberry Perl 发行版,以防 ActivePerl 做一些不同的事情


我正在尝试编写一个 Perl 脚本,该脚本调用许多 PowerShell 片段并依赖于 Strawberry Perl 发行版。

PowerShell 相当方便地具有一个 -encodedCommand 标志,该标志接受 base64 编码的字符串,然后对其进行处理。这有助于避免与引用相关的问题。

我尝试了可能有效的最简单的方法。

// powersheller.pl

#! /usr/bin/env perl

use strict;
use warnings;

use MIME::Base64;
use Encode qw/encode decode/;

use vars ('$powershell_command');

sub run_powershell_fragment {
    my ($contents) = @_;
    my $encoded = encode_base64($contents);
    printf "encoded: %s\n", $encoded;
    return `powershell.exe -noprofile -encodedCommand $encoded`;
}

printf "%s\n---\n", run_powershell_fragment($powershell_command);

BEGIN {
$powershell_command = <<EOF
echo "hi"   
EOF
}

并运行它。这是在 powershell 窗口中运行 perl 脚本的...标准输出 channel (?)的输出。

PS C\...> perl .\powersheller.pl
encoded: ZWNobyAiaGkiCQo=

Redundant argument in printf at .\powersheller.pl line 18.
?????? : The term '??????' is not recognized as the name of a cmdlet, function, script file, or operable program.

---

这看起来像是一个编码问题。我猜测 Perl 默认使用类似于 utf-8 的东西,而 powershell 期望使用 utf16-le 或类似的东西。

sub run_powershell_fragment {
    my ($contents) = @_;
    my $utf16_le_contents = encode("utf-16le", $contents);
    my $encoded = encode_base64($utf16_le_contents);
    printf "encoded: %s\n", $encoded;
    return `powershell.exe -noprofile -encodedCommand $encoded`;
}

从技术上讲,使用“ucs-2le”也可以。不知道哪个合适。

无论如何,在插入额外的转换后,程序可以按预期工作。

PS C:\...> perl .\powersheller.pl
encoded: ZQBjAGgAbwAgACIAaABpACIACQAKAA==

hi

---

为什么我需要做的就是这一切? Perl 处理与 argv 和 stdout &c 相关的转换吗?

最佳答案

qx`` 不执行任何转换。该命令预计将使用系统的 ANSI 代码页进行编码,因为它将不经修改地传递给 CreateProcessA 或类似函数。[1]

use Encode qw( encode );
use Win32  qw( );

my $cmd_ansi = encode("cp".Win32::GetACP(), $cmd);
`$cmd_ansi`

当然,如果命令只包含 ASCII 字符,编码就没有意义了。


同样,@ARGV 中的值尚未解码。它们是从系统接收的,并使用系统的 ANSI 代码页进行编码。

use Encode qw( decode );
use Win32  qw( );

my @decode_argv = map { decode("cp".Win32::GetACP(), $_) } @ARGV;

当然,如果参数仅包含 ASCII 字符,则解码没有意义。


默认情况下,文件句柄不执行任何编码或解码,除了 CRLF ⇔ LF 转换(CRLF ⇒ LF 读取,LF ⇒ CRLF 写入)。您需要向 print/printf/say< 提供一串字节(值为 0..255 的字符串)< support>[1],您将从 readline/read/readpipe 收到一串字节。

您可以在打开文件时提供编码/解码层。

open(my $fh, '>:encoding(UTF-8)', $qfn)

您可以通过 open 提供默认的编码/解码层杂注。

use open ':encoding(UTF-8)';
open(my $fh, '>', $qfn)

在这两种情况下,您现在都需要提供一串 Unicode 代码点来 print/printf/say,然后您将类似地,从 readline/read/readpipe 接收一串字节。

我不确定什么最适合 STDIN/STDOUT/STDERR,但您可以从以下内容开始:

use Win32 qw( );
my ($in_enc, $out_enc);
BEGIN {
   $in_enc  = "cp".Win32::GetConsoleCP();
   $out_enc = "cp".Win32::GetConsoleOutputCP();
   binmode STDIN,  ":encoding($in_enc)";
   binmode STDOUT, ":encoding($out_enc)";
   binmode STDERR, ":encoding($out_enc)";
}

您应该使用 UTF-16le 而不是 UCS-2le。


  1. 如果您提供的字符串包含非字节(0..255 之外的字符),Perl 将假定您打算使用 UTF-8 对该字符串进行编码。它将发出警告(“宽字符”)并使用 utf8 对字符串进行编码。

关于windows - Strawberry Perl——默认情况下编码转换在哪里完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52941626/

相关文章:

c++ - Windows C++ 字符显示不正确,只显示一些未知字符

c++ - Visual Studio C++ 为什么不编译 0b00000001

windows - 站点到站点 VPN 与点到站点 VPN

windows - 如何从 SQLCMD 输出中删除空白

Python 解释器在 Powershell ISE 中崩溃

email - 在 Powershell 中复制打开的文件

perl - 将 XS 代码集成到 Moose 类定义文件中

powershell - 如何在 Powershell 中使用带有 Where-Object 的参数化脚本 block ?

perl - 各种$ SIG {CHLD}值之间有什么区别?

perl - 用 Perl 编写宏