Strawberry Perl –默认情况下,编码转换在哪里完成?

时间:2018-10-23 05:17:33

标签: windows powershell perl unicode

基本上,我编写了一个Perl脚本,该脚本为Powershell创建了一个编码命令并尝试运行它。在base64编码之前,我必须将命令字符串显式转换为utf-16。我想知道为什么为什么要全部使脚本正常工作。 Windows *上的Perl默认在与控制台甚至是文件系统交互的“普通”程序的运行中执行哪些转换?例如,argv是否已转换? stdin / stdout是否已转换?文件IO是否会进行转换?

✱特别是草莓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脚本的...标准输出通道(?)的输出。

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

1 个答案:

答案 0 :(得分:3)

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 [1] 提供一个字节字符串(值在0..255中的字符串)。 ,您将从readline / read / readpipe中收到一串字节。

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

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

您可以通过open编译指示提供默认的编码/解码层。

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

在两种情况下,您现在都需要向print / printf / say提供一个Unicode代码点字符串,并且类似地,您将从{ {1}} / readline / read

我不确定STDIN / STDOUT / STDERR的最佳选择,但是您可以从以下内容开始:

readpipe

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


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