编辑 - 答案张贴在下方
我有一个脚本,通常使用 @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::API
和 CommandLineToArgvW
。它是故意冗长的,希望对于像我这样对 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/