【问题标题】:Strawberry Perl -- where are encoding conversions done by default?Strawberry Perl - 默认情况下在哪里进行编码转换?
【发布时间】:2019-03-27 06:05:00
【问题描述】:

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

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


我正在尝试编写一个调用许多 PowerShell 片段并依赖于 Strawberry Perl 分发的 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 脚本时...标准输出通道 (?) 的输出。

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 相关的转换?

【问题讨论】:

    标签: windows powershell perl unicode


    【解决方案1】:

    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 on read,LF ⇒ CRLF on write)。您应该向print/printf/say[1] 提供一串字节(值在 0..255 中的字符串),您将收到一个来自readline/read/readpipe的字节串。

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

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

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

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

    在这两种情况下,您现在都需要向print/printf/say 提供一串 Unicode 代码点,同样您将收到来自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 对字符串进行编码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-23
      • 2016-07-06
      • 1970-01-01
      • 2012-12-11
      • 1970-01-01
      相关资源
      最近更新 更多