windows - 将字符串解析为 ARGV 等效项(Windows 和 Perl)

标签 windows perl arguments

编辑 - 答案张贴在下方

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

例子:

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

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

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

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"我宁愿不担心边缘情况,但我知道如果我不这样做,总有一天会困扰我。

最佳答案

回答 - 谢谢 ikegami!

我在下面发布了一个工作程序,它根据 ikegami 的建议使用 shell32.dll 中的 Win32::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);   
}

关于windows - 将字符串解析为 ARGV 等效项(Windows 和 Perl),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29268118/

相关文章:

c# - 如何知道 Windows 何时启动或关闭?

windows - 使用 Github for Windows 通过 SSH 使用自己的私有(private) Git

linux - 如何在csv中将列的所有数据放在一行中

perl - 如何正确捕获unix命令的返回值?

javascript - Angular.js 中的过滤指令

java - Java中如何通过反射获取方法参数的值?

windows - 检查命令是否在CMD中是内部的

Windows批处理命令从一个文件夹中删除一个文件以外的文件

java - 从 perl 调用 java 类

c - 作为 const 或操作数的正确参数