Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ember.js/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何安全地将带有空格的文件名传递给Perl中的外部命令?_Perl_Escaping - Fatal编程技术网

如何安全地将带有空格的文件名传递给Perl中的外部命令?

如何安全地将带有空格的文件名传递给Perl中的外部命令?,perl,escaping,Perl,Escaping,我有一个处理大量文件名的Perl脚本,并在backticks中使用这些文件名。但文件名包含空格、撇号和其他时髦的字符 我希望能够正确地避开它们(即不使用头顶上的随机正则表达式)。是否有一个CPAN模块可以正确地转义字符串以用于bash命令?我知道我以前解决过这个问题,但这次我找不到任何关于它的东西。关于它的信息似乎少得出奇。你在找什么 返回EXPR的值,并将所有非“word”字符反斜杠 更新:正如霍布斯在评论中指出的那样,quotemeta并非用于此目的,仔细考虑一下,嵌入式nuls可能存在问题

我有一个处理大量文件名的Perl脚本,并在backticks中使用这些文件名。但文件名包含空格、撇号和其他时髦的字符

我希望能够正确地避开它们(即不使用头顶上的随机正则表达式)。是否有一个CPAN模块可以正确地转义字符串以用于bash命令?我知道我以前解决过这个问题,但这次我找不到任何关于它的东西。关于它的信息似乎少得出奇。

你在找什么

返回EXPR的值,并将所有非“word”字符反斜杠

更新:正如霍布斯在评论中指出的那样,
quotemeta
并非用于此目的,仔细考虑一下,嵌入式
nul
s可能存在问题。另一方面,当遇到嵌入式
null
s时,会发出咯咯声

最安全的方法是完全避开外壳。使用的列表形式可以实现这一目标(几个月前,我沮丧地发现,
cmd.exe
可能仍然会涉及Windows),我建议这样做

如果您需要命令的输出,最好(安全方面)自己打开一个管道,如所示,如果您可以管理它(即,如果您直接调用某个命令,而不需要任何shell脚本或高级重定向把戏),最安全的做法是避免数据完全通过shell传递

在perl 5.8+中:

my @output_lines = do {
    open my $fh, "-|", $command, @args or die "Failed spawning $command: $!";
    <$fh>;
};
my@output\u lines=do{
打开我的$fh、“-|”、$command、@args或die“未能生成$command:$!”;
;
};
如果有必要支持5.6:

my @output_lines = do {
    my $pid = open my $fh, "-|";
    die "Couldn't fork: $!" unless defined $pid;
    if (!$pid) {
        exec $command, @args or die "Eek, exec failed: $!";
    } else {
        <$fh>; # This is the value of the C<do>
    }
};
my@output\u lines=do{
my$pid=打开my$fh,“-|”;
die“无法分叉:$!”除非定义$pid;
如果(!$pid){
exec$命令,@args或die“Eek,exec失败:$!”;
}否则{
这是C的值
}
};
有关此类业务的更多信息,请参见
perldoc-perlipc
,也可参见
IPC::Open2
IPC::Open3

tl;dr

以下子例程在类Unix和Windows系统上安全地引用(转义)文件名(路径)列表:

#!/usr/bin/env perl

sub quoteforshell { 
  return join ' ', map { 
    $^O eq 'MSWin32' ?
      '"' . s/"/""/gr . '"'
      : 
      "'" . s/'/'\\''/gr . "'" 
  } @_;
}

#'# Sample invocation
my $shellcmd = ($^O eq 'MSWin32' ? 'echo ' : 'printf "%s\n" ') . 
  quoteforshell('\\foo/bar', 'I\'m here', '3" of snow', 'bar |&;()<>#!');

print `$shellcmd`;
sub qxnoshell {
  use IPC::Cmd;
  return unless @_;
  my @cmdargs = @_;
  if ($^O eq 'MSWin32') { # Windows
    # Ensure that the executable name ends in '.exe'
    $cmdargs[0] .= '.exe' unless $cmdargs[0] =~ m/\.exe$/i;
    unless (IPC::Cmd::can_run $cmdargs[0]) { # executable not found
      # Issue warning, as qx// would and open '-|' below does.
      my $warnmsg = "Executable '$cmdargs[0]' not found";
      scalar(caller) eq 'main' ? warn($warnmsg . "\n") : warnings::warnif('exec', $warnmsg);
      return; 
    }
    for (@cmdargs[1..$#cmdargs]) {
      if (m'"') {
        s/"/\\"/; # \-escape embedded double-quotes
        $_ = '"' . $_ . '"'; # enclose as a whole in embedded double-quotes
      }
    }
  }
  open my $fh, '-|', @cmdargs or return;
  my @lines = <$fh>;
  close $fh;
  return wantarray ? @lines : join('', @lines);
}
示例(使用大多数POSIX外壳元字符):

请注意每个输入字符串是如何用单引号括起来的,以及所有输入字符串中唯一需要引号的字符是
,如前所述,它被替换为
,\'

这将按原样输出输入字符串,每行一个:

\foo/bar
I'm here
3" of snow
bar |&;()<>#!
这类似于上面的
quoteforsh()
,除了

  • 双引号用于将标记括起来,因为
    cmd.exe
    不支持单引号
  • 唯一需要转义的字符是
    ”,它被转义为
    ——但是,请注意,对于文件名,这并不是严格必需的,因为Windows不允许在文件名中使用
    实例
但是,也有一些限制和陷阱:

#!/usr/bin/env perl

sub quoteforshell { 
  return join ' ', map { 
    $^O eq 'MSWin32' ?
      '"' . s/"/""/gr . '"'
      : 
      "'" . s/'/'\\''/gr . "'" 
  } @_;
}

#'# Sample invocation
my $shellcmd = ($^O eq 'MSWin32' ? 'echo ' : 'printf "%s\n" ') . 
  quoteforshell('\\foo/bar', 'I\'m here', '3" of snow', 'bar |&;()<>#!');

print `$shellcmd`;
sub qxnoshell {
  use IPC::Cmd;
  return unless @_;
  my @cmdargs = @_;
  if ($^O eq 'MSWin32') { # Windows
    # Ensure that the executable name ends in '.exe'
    $cmdargs[0] .= '.exe' unless $cmdargs[0] =~ m/\.exe$/i;
    unless (IPC::Cmd::can_run $cmdargs[0]) { # executable not found
      # Issue warning, as qx// would and open '-|' below does.
      my $warnmsg = "Executable '$cmdargs[0]' not found";
      scalar(caller) eq 'main' ? warn($warnmsg . "\n") : warnings::warnif('exec', $warnmsg);
      return; 
    }
    for (@cmdargs[1..$#cmdargs]) {
      if (m'"') {
        s/"/\\"/; # \-escape embedded double-quotes
        $_ = '"' . $_ . '"'; # enclose as a whole in embedded double-quotes
      }
    }
  }
  open my $fh, '-|', @cmdargs or return;
  my @lines = <$fh>;
  close $fh;
  return wantarray ? @lines : join('', @lines);
}
  • 不能禁止对现有环境变量引用的解释,例如
    %USERNAME%
    ;相比之下,不存在的变量或孤立的
    %
    实例就可以了。
    • 注意:您应该能够将
      %%
      实例转义为
      %%
      ,但尽管这在批处理文件中有效,但在Perl中却无法正常工作:
      • `perl“%%USERNAME%%.pl”`
        投诉,例如关于未找到
        %jdoe%.pl
        ,这意味着插入了
        %USERNAME%%
        ,尽管字符数增加了一倍
      • (另一方面,双引号字符串中孤立的
        %
        实例不需要像批处理文件中那样转义。)
  • 将嵌入式
    实例转义为
    是唯一安全的方法,但这不是大多数目标程序所期望的。
    • 令人难以置信的是,在Windows上,所需的转义最终取决于目标程序-有关完整背景,请参阅
    • 简言之,困境是:
      • 如果您为目标程序转义,包括Perl在内的大多数程序,除了
        \“
        ”,那么部分参数 列表可能永远不会传递给目标程序,剩余部分可能会导致失败、不必要的文件重定向,或者更糟糕的是,意外执行任意命令
      • 如果为
        cmd.exe
        转义,可能会中断目标程序的解析
      • 你不能两者都逃脱
      • 如果您的命令根本不需要涉及shell,那么您可以解决这个问题—请参见下文

备选方案:无shell命令调用

如果您的命令是对单个可执行文件的调用,并且所有参数都要按原样传递,则根本不需要涉及shell,这:

  • 不需要引用参数,这明显绕过了Windows上的
    -引用问题
  • 通常效率更高
以下子例程适用于类Unix系统和Windows系统,是
qx/
`…`
的无外壳替代程序,它接受命令作为要按原样解释的参数列表进行调用:

#!/usr/bin/env perl

sub quoteforshell { 
  return join ' ', map { 
    $^O eq 'MSWin32' ?
      '"' . s/"/""/gr . '"'
      : 
      "'" . s/'/'\\''/gr . "'" 
  } @_;
}

#'# Sample invocation
my $shellcmd = ($^O eq 'MSWin32' ? 'echo ' : 'printf "%s\n" ') . 
  quoteforshell('\\foo/bar', 'I\'m here', '3" of snow', 'bar |&;()<>#!');

print `$shellcmd`;
sub qxnoshell {
  use IPC::Cmd;
  return unless @_;
  my @cmdargs = @_;
  if ($^O eq 'MSWin32') { # Windows
    # Ensure that the executable name ends in '.exe'
    $cmdargs[0] .= '.exe' unless $cmdargs[0] =~ m/\.exe$/i;
    unless (IPC::Cmd::can_run $cmdargs[0]) { # executable not found
      # Issue warning, as qx// would and open '-|' below does.
      my $warnmsg = "Executable '$cmdargs[0]' not found";
      scalar(caller) eq 'main' ? warn($warnmsg . "\n") : warnings::warnif('exec', $warnmsg);
      return; 
    }
    for (@cmdargs[1..$#cmdargs]) {
      if (m'"') {
        s/"/\\"/; # \-escape embedded double-quotes
        $_ = '"' . $_ . '"'; # enclose as a whole in embedded double-quotes
      }
    }
  }
  open my $fh, '-|', @cmdargs or return;
  my @lines = <$fh>;
  close $fh;
  return wantarray ? @lines : join('', @lines);
}
  • 由于使用了
    IPC::Cmd
    ,需要Perl v5.9.5+
  • 请注意,子例程很难在Windows上运行:
    • 即使参数作为列表传递,
      open…,“-|”
      在Windows上,如果初始调用尝试失败,仍然会使用
      cmd.exe
      ——这同样适用于
      system()
      exec()
      # Unix: $out should receive literal '$$', which demonstrates that
      # /bin/sh is not involved.
      my $out = qxnoshell 'printf', '%s', '$$' 
      
      # Windows: $out should receive literal '%USERNAME%', which demonstrates
      # that cmd.exe is not involved.
      my $out = qxnoshell 'perl', '-e', 'print "%USERNAME%"'