为什么这张地图功能如此复杂?

时间:2014-11-26 17:40:24

标签: perl

当我运行以下脚本时,我得到了

$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;

2 个答案:

答案 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);
相关问题