当我运行以下脚本时,我得到了
$VAR1 = 'ssh -o Compression=yes -o ConnectTimeout=333 remoteIp \'mbuffer -q -s 128k -m mbufferSize -4 -I mbufferPort|zfs recv recvOpt dstDataSet\'';
这让我想到,所有$shellQuote
都会将数组转换为字符串并在开头和结尾添加'
。另外在两个数组之间添加|
。但是我不知道map
函数的用途。
该脚本是this的超简化版本,目的是确定$shellQuote
的确切含义。
问题
$shellQuote
看起来非常复杂。它还有什么我想念的吗?
#!/usr/bin/perl
use Data::Dumper;
use warnings;
use strict;
my $shellQuote = sub {
my @return;
for my $group (@_){
my @args = @$group;
for (@args){
s/'/'"'"'/g;
}
push @return, join ' ', map {/^[-\/@=_0-9a-z]+$/i ? $_ : qq{'$_'}} @args;
}
return join '|', @return;
};
sub buildRemoteRefArray {
my $remote = shift;
my @sshCmdArray = (qw(ssh -o Compression=yes -o), 'ConnectTimeout=' . '333');
if ($remote){
return [@sshCmdArray, $remote, $shellQuote->(@_)];
}
return @_;
};
my @recvCmd = buildRemoteRefArray('remoteIp', ['mbuffer', (qw(-q -s 128k -m)), 'mbufferSize', '-4', '-I', 'mbufferPort'], ['zfs', 'recv', 'recvOpt', 'dstDataSet']);
my $cmd = $shellQuote->(@recvCmd);
print Dumper $cmd;
答案 0 :(得分:3)
map
函数,我认为你的意思是
map {/^[-\/@=_0-9a-z]+$/i ? $_ : qq{'$_'}} @args
检查每个参数以查看它是否是合法的shell令牌。法律外壳代币通过;带有可疑字符的任何内容都会附在“引号”上。
请记住,您的示例有两次调用$shellQuote
,而不只是一次;你在打印:
print Dumper($shellQuote->(
[
qw(ssh -o Compression=yes -o),
'ConnectTimeout=' . '333',
'remoteIp',
$shellQuote->(
[
'mbuffer',
(qw(-q -s 128k -m)),
'mbufferSize',
'-4',
'-I',
'mbufferPort',
],
[
'zfs',
'recv',
'recvOpt',
'dstDataSet',
],
),
]
));
为了清楚列表的结构,我将每个shell命令的参数缩进比命令更进一步。所以你的引号来自外部$shellQuote
,这意味着内部$shellQuote
已将空格放入其结果中; |
来自内部$shellQuote
,它正在使用它们来组合传递给它的两个数组引用。
打破map
功能,map { expr } @args
表示expr
的每个元素的'评估@args
,并列出结果。
/^[-\/@=_0-9a-z]+$/i ? $_ : qq{'$_'}
是一个三元表达式(Googleable术语)。 $_
是@args
的当前元素,/re/i
当且仅当$_
与给定的正则表达式(Googleable term)匹配时才会为true(不区分大小写)。整个表达式表示'如果@args
的当前元素仅包含列出的字符(ASCII字母,ASCII数字,字符-
,/
,@
和{ {1}}),按原样返回;否则将其包裹在单引号中。
=
循环,在此之前,用for
替换@args
的每个元素中的每个元素,这是将单引号嵌入到单引号字符串中的一种特殊方式SH
答案 1 :(得分:2)
暂时忽略你的代码并查看这个代码,因为它更清晰一些。
# shell_quote_arg("foo bar") => 'foo bar'
sub shell_quote_arg {
my ($s) = @_;
return $s if $s !~ /[^-\/@=_0-9a-z]/i;
$s =~ s/'/'"'"'/g; # '
return qq{'$s'}
}
# shell_quote("echo", "foo bar") => echo 'foo bar'
sub shell_quote {
return join ' ', map { shell_quote_arg($_) } @_;
}
my $remote_shell_cmd1 = shell_quote('mbuffer', 'arg1a', 'arg1b');
my $remote_shell_cmd2 = shell_quote('zfs', 'arg2a', 'arg2b');
my $remote_shell_cmd = join(' | ', $remote_shell_cmd1, $remote_shell_cmd2);
my $local_shell_cmd = shell_quote('ssh', $host, $remote_shell_cmd);
我的shell_quote
用于根据程序名称和参数构建shell命令。例如,
shell_quote('zfs', 'recv', 'recvOpt', 'dstDataSet')
返回
zfs recv recvOpt dstDataSet
那么为什么不使用join(' ', 'zfs', 'recv', 'recvOpt', 'dstDataSet')
呢?因为空格,$
和'
等字符对shell具有特殊含义。如果存在,shell_quote
需要做额外的工作。例如,
shell_quote('echo', q{He's got $100})
返回
echo 'He'"'"'s got $100' # When run, uses echo to output: He's got $100
您展示的shellQuote
与我的shell_quote
完全相同,但它也会在我的代码中看到join('|', ...)
。
顺便提一下,请注意shellQuote
被调用两次。第一次,它用于构建在远程计算机上执行的命令,如下所示:
my $remote_shell_cmd1 = shell_quote('mbuffer', 'arg1a', 'arg1b');
my $remote_shell_cmd2 = shell_quote('zfs', 'arg2a', 'arg2b');
my $remote_shell_cmd = join(' | ', $remote_shell_cmd1, $remote_shell_cmd2);
第二次,它用于构建在本地计算机上执行的命令,如下所示:
my $local_shell_cmd = shell_quote('ssh', $host, $remote_shell_cmd);