使用多个参数调用shell命令

时间:2018-05-27 14:17:26

标签: perl

我正在尝试通过Perl脚本自动创建证书。

我想要运行的命令是:

easyrsa build-client-full $clientname nopass

我认为应该在Perl中完成的方式是:

   my $arguments = ("build-client-full $clientname nopass");       
   my $cmd = "$easyrsa_path/easyrsa"." "."$arguments";
   system("bash", $cmd);

然而,这会产生

  

“找不到档案”

执行时。我三重检查路径是否正确。

如果我这样试试:

   my @arguments = ("bash", $easyrsa_path,"build-client-full $clientname nopass");
   system(@arguments);

Bash返回

  

“未知命令'build-client-full test nopass'。无命令运行   用于帮助。“

2 个答案:

答案 0 :(得分:3)

背景

当你使用system(LIST) LIST有多个元素时,Perl不会调用shell,而是直接调用LIST中第一个元素给出的程序,使用列表的其余部分作为命令行参数传递 verbatim ,使用 no 插入shell,包括 no 在空格上拆分参数。

所以在你的第一个例子中,Perl运行命令bash并传递字符串"$easyrsa_path/easyrsa build-client-full $clientname nopass",字面意思是一个大长参数,在第二个例子中,它& #39;运行命令bash并传递两个参数$easyrsa_path"build-client-full $clientname nopass"。但是,我假设easyrsa需要将三个参数作为其参数列表中的单独字符串,shell通常会拆分,但由于您对system的两次调用都没有使用shell,它没有用。

根据文档,

system(和exec)有四种解释其论点的方法:

  1. 如果传递一个不包含任何shell元字符的单个字符串(包括只有一个元素的LIST),它将被拆分为单词并直接传递给execvp(3)(意思是它)绕过壳)。 警告:此调用很容易与以下内容混淆 - 单个元字符将导致调用shell,这可能很危险,尤其是当未经检查的变量插入到命令字符串中时。

  2. 如果传递包含shell元字符的单个字符串(包括只有一个元素的LIST),则整个参数将传递给系统的命令shell进行解析。通常,在Unix平台上有/bin/sh -c,但是the idea of the "default shell" is problematic,并且肯定不能保证它会bash(尽管可以 be)。

    警告:system的调用中,您拥有shell的全部功能,这也意味着您负责正确引用和转义任何shell元字符和/或空白。如果明确要 shell的强大功能,我建议您使用此表单,否则,通常最好使用以下两种方法之一。< / p>

  3. 如果LIST中有多个参数,则使用execvp(3)中的参数调用LIST,这意味着避免使用shell。 (有关Windows上的警告,请参阅下文。)

  4. 表单system {EXPR} LIST 始终运行EXPR命名的程序,并避免使用shell,无论LIST中的内容是什么。 (有关Windows上的警告,请参阅下文。)

  5. 如果你想传递shell通常会解释的特殊字符,那么后两者是可取的,我实际上总是建议这样做,因为盲目地将用户输入传递到system可以打开一个安全性我写了一篇关于over on PerlMonks的文章。

    解决方案

    @Borodin和@AnFi已经指出:如果您只是正确地分割LIST的元素,它应该有用 - 看起来您不需要{{1}的任何功能或者这里的任何shell。并且不要忘记检查错误!

    bash

    请注意,有很好的模块提供alternatives to system and qx,我的首选模块通常为IPC::Run3。如果要捕获外部命令的输出,这些模块非常有用。在这种情况下,IPC::System::Simple可能会更容易,因为它为system("$easyrsa_path/easyrsa","build-client-full",$clientname,"nopass") == 0 or warn "system failed: \$? = $?"; 提供了更好的错误处理的替代品,以及始终避免使用shell的system。 (当您说systemx时,该模块是autodie使用的模块。)

    use autodie ':all';

    请注意,如果确实想要致电use IPC::System::Simple qw/systemx/; systemx("$easyrsa_path/easyrsa","build-client-full",$clientname,"nopass"); ,您需要添加bash选项并说出-c。但正如我上面所说,我强烈建议不要这样做,因为如果system("bash","-c","--","$easyrsa_path/easyrsa build-client-full $clientname nopass")$easyrsa_path包含任何shell元字符或恶意内容,您可能最终会遇到一个大问题。

    Windows比上面的更复杂。文档说明唯一可靠的&#34;避免调用shell的方法是$clientname形式,但在Windows上,命令行参数不作为列表传递,而是作为单个大字符串传递,并且它由被调用的命令决定,而不是shell,解释该字符串,以及不同的命令可能会有不同的做法 - see also。 (不过,我听过Win32::ShellQuote的好消息。)

    另外,还有perlport中记录的特殊system PROGRAM LIST表单。

答案 1 :(得分:-1)

如果将多个参数传递给system,则每个参数都会形成命令行的单独参数。所以就好像你已经进入了

easyrsa "build-client-full test nopass"

并且您正确地收到了错误

  

未知命令&#39; build-client-full test nopass&#39;

您也不需要添加bash:perl会在必要时为您运行shell

您可以将整个命令传递给system

system($cmd)

和perl将它传递给shell进行处理,就像你在命令提示符下输入它一样。或者您可以正确拆分参数

system("$easyrsa_path/easyrsa", "build-client-full", $clientname, "nopass")

将直接调用perl easyrsa,除非该命令包含需要shell处理的内容,例如输出重定向