【问题标题】:Parsing string into ARGV equivalent (Windows and Perl)将字符串解析为 ARGV 等效项(Windows 和 Perl)
【发布时间】:2015-03-27 12:41:13
【问题描述】:

编辑 - 答案贴在下面

我有一个通常使用@ARGV 参数的脚本,但在某些情况下,它由另一个脚本(我无法修改)调用,而该脚本只传递了一个配置文件名,其中除其他外还有本应使用的命令行选项直接通过。

例子:

Args=--test --pdf "C:\testing\my pdf files\test.pdf"

如果可能的话,我想要一种方法将此字符串解析为与@ARGV 相同的数组。

我有一个解决方法,我设置了一个只回显@ARGV 的外部 perl 脚本,然后我像下面这样调用这个脚本(标准样板已删除)。

echo-args.pl

print join ("\n", @ARGV);

test-echo-args.pl

$my_args = '--test --pdf "C:\testing\my pdf files\test.pdf"';
@args = map { chomp ; $_ } `perl echo-args.pl $my_args`;

这看起来不优雅,但它确实有效。有没有更好的方法而不调用新流程?我确实尝试过拆分和处理,但是命令行上有一些奇怪的地方,例如-a"b c" 变成 '-ab c'-a"b"" 变成 -ab",我宁愿不担心边缘情况,但我知道如果我不这样做,那总有一天会咬我。

【问题讨论】:

  • 看起来你需要使用反引号或者system来执行命令行,但是以Args为参数的应用是什么?
  • 我不确定这与问题有什么关系,但 test-echo-args.pl 就是这样一个应用程序的一个例子。假设字符串 $my_args 来自文件。我们如何将该字符串解析为数组?
  • 我试图了解什么是脚本,什么是另一个脚本,以及Args=--test ... 是一个例子。只有你知道答案,你才清楚!如果您要编写一个可以使用其命令行参数的程序,或者从STDIN 读取(如果没有),那么您只需要编写if ( @ARGV ) { ... } 但我怀疑您的问题不止于此
  • 程序 A 设计为正常使用@ARGV。程序 B 将调用程序 A,但不会使用正确的命令行。相反,它将字符串写入配置文件,并在命令行上提供配置文件名。然后程序 A 需要读取这个配置文件,找到参数字符串并处理它。由于我们已经有了解析@ARGV 的代码,我们希望重用它而不是重新发明轮子。我们将通过将字符串拆分为数组来做到这一点。上面的示例以简化的方式显示了这种情况。显示其他细节会偏离重点。
  • 提示:while ($num > 0) { --$num; ... } => while ($num--) { ... }。或者在这种情况下,for (1..$num) { ... }

标签: windows perl arguments


【解决方案1】:

回答 - 感谢 ikegami!

根据 ikegami 的建议,我在下面发布了一个使用来自shell32.dllWin32::APICommandLineToArgvW 的工作程序。它是故意冗长的,希望对于像我这样对 C 和指针算法非常生疏的人来说更容易理解。

欢迎任何提示,除了明显的简化:)

use strict;
use warnings;

use Encode     qw( encode decode );
use Win32::API qw( );
use Data::Dumper;

# create a test argument string, with some variations, and pack it
# apparently an empty string returns $^X which is documented so check before calling
my $arg_string = '--test 33 -3-t"  "es 33\t2 ';
my $packed_arg_string = encode('UTF-16le', $arg_string."\0");

# create a packed integer buffer for output
my $packed_argc_buf_ptr = pack('L', 0);

# create then call the function and get the result
my $func = Win32::API->new('shell32.dll', 'CommandLineToArgvW', 'PP', 'N')
   or die $^E;
my $ret = $func->Call($packed_arg_string, $packed_argc_buf_ptr);

# unpack to get the number of parsed arguments
my $argc = unpack('L', $packed_argc_buf_ptr);
print "We parsed $argc arguments\n";

# parse the return value to get the actual strings
my @argv = decode_LPWSTR_array($ret, $argc);
print Dumper \@argv;

# try not to leak memory
my $local_free = Win32::API->new('kernel32.dll', 'LocalFree', 'N', '')
    or die $^E; 
$local_free->Call($ret);

exit;

sub decode_LPWSTR_array {
    my ($ptr, $num) = @_;

    return undef if !$ptr;

    # $ptr is the memory location of the array of strings (i.e. more pointers)
    # $num is how many we need to get
    my @strings = ();

    for (1 .. $num) {
        # convert $ptr to a long, using that location read 4 bytes - this is the pointer to the next string
        my $string_location = unpack('P4', pack('L', $ptr));        
        # make it human readable
        my $readable_string_location = unpack('L', $string_location);       
        # decode the string and save it for later
        push(@strings, decode_LPCWSTR($readable_string_location));

        # our pointers are 32-bit
        $ptr += 4;      
    }
    return @strings;
}

# Copied from http://stackoverflow.com/questions/5529928/perl-win32api-and-pointers
sub decode_LPCWSTR {
   my ($ptr) = @_;

   return undef if !$ptr;

   my $sW = '';
   for (;;) {
      my $chW = unpack('P2', pack('L', $ptr));
      last if $chW eq "\0\0";
      $sW .= $chW;
      $ptr += 2;
   }

   return decode('UTF-16le', $sW);   
}

【讨论】:

    【解决方案2】:

    在 unix 系统中,是 shell 将 shell 命令解析为字符串。但在 Windows 中,这取决于每个应用程序。我认为这通常是使用CommandLineToArgv 系统调用(您可以在Win32::API 的帮助下调用)来完成的,但是如果您想自己重新实现它,规范会记录在here 中。

    【讨论】:

    • 谢谢,即使我不能使用它也会很有趣。我会试一试,稍后再更新。我知道在通配符扩展方面与 unix 存在一些差异,但在搜索时无法找到该规范。
    • 非常感谢,我已经编辑添加了一些工作代码。我正要放弃,直到我在 stackoverflow.com/questions/5529928/perl-win32api-and-pointers 看到你的答案,其中有我可以使用的非常有用的编码/解码/打包/解包示例。
    猜你喜欢
    • 1970-01-01
    • 2014-07-19
    • 2013-02-04
    • 1970-01-01
    • 2021-06-17
    • 2013-06-22
    • 2015-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多