代码之家  ›  专栏  ›  技术社区  ›  Fang

使用多个参数调用shell命令

  •  1
  • Fang  · 技术社区  · 6 年前

    我正在尝试通过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 回复  |  直到 6 年前
        1
  •  3
  •   haukex    6 年前

    背景

    当你使用 system(LIST) 在哪里? LIST ,并将列表的其余部分用作要传递的命令行参数 逐字 壳插值,包括 在空白处拆分参数。

    所以在第一个示例中,Perl正在运行命令 bash 传递字符串 "$easyrsa_path/easyrsa build-client-full $clientname nopass" ,字面意思是 大长参数,在第二个示例中,它运行命令 猛击 传递这两个论点 $easyrsa_path "build-client-full $clientname nopass" . 不过,我想 easyrsa 需要将这三个参数作为参数列表中的单独字符串,shell通常会将其拆分,但由于您对 system 不使用外壳,它不工作。

    system (和) exec )根据文件,有四种解释其论点的方法:

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

    2. 如果传递一个字符串(包括 只有一个元素)包含shell元字符,整个参数将传递给系统的命令shell进行分析。一般来说,那是 /bin/sh -c 在Unix平台上,但是 the idea of the "default shell" is problematic 当然也不能保证 猛击 (尽管如此 能够 是)

      警告: 在这个调用中 系统 您拥有shell的全部功能,这也意味着您有责任正确地引用和转义shell元字符和/或空白。我向你推荐 只有 如果你 明确要求 外壳的力量,否则,通常最好使用下面两个中的一个。

    3. 如果在 ,这个电话 执行副总裁(3) 也就是说,外壳是可以避免的。 (有关窗户的注意事项,请参见下文。)

    4. 形式 system {EXPR} LIST 总是 运行由命名的程序 EXPR 不管里面有什么,都要避开外壳。 (有关窗户的注意事项,请参见下文。)

    如果您想传递shell通常会解释的特殊字符,那么后两个字符是可取的,而且我实际上建议您这样做,因为盲目地将用户输入传递到 系统 可以打开一个安全漏洞-我写了一篇关于这个的长篇文章 over on PerlMonks .

    解决

    正确地说,它应该可以工作-看起来你不需要 猛击 或者这里的贝壳。别忘了检查错误!

    system("$easyrsa_path/easyrsa","build-client-full",$clientname,"nopass") == 0
        or warn "system failed: \$? = $?";
    

    请注意,有一些好的模块提供 alternatives to system and qx ,我的转到模块通常是 IPC::Run3 . 如果您想从外部命令捕获输出,这些模块非常有用。在这种情况下, IPC::System::Simple 可能更容易,因为它为 系统 以及更好的错误处理 systemx 它总是避开外壳。(这个模块是什么? autodie 当你说的时候使用 use autodie ':all'; )

    use IPC::System::Simple qw/systemx/;
    systemx("$easyrsa_path/easyrsa","build-client-full",$clientname,"nopass");
    

    注意如果你 真的? 想打电话来 猛击 ,您需要添加 -c 选择并说 system("bash","-c","--","$easyrsa_path/easyrsa build-client-full $clientname nopass") . 但正如我上面所说,我强烈建议不要这样做,因为如果 $easyrsa_路径 $clientname 包含任何shell元字符或恶意内容,最终可能会出现严重问题。

    窗户

    窗户比上面更复杂。文档中说,避免调用shell的唯一“可靠”方法是 system PROGRAM LIST 窗体,但在Windows上,命令行参数不是作为列表传递的,而是单个大字符串,由被调用的命令而不是shell来解释该字符串,不同的命令可能会做不同的事情。- see also . (我听说过关于 Win32::ShellQuote 尽管如此。)

    system(1, @args) 表格记录在 perlport .

        2
  •  -1
  •   zdim    6 年前

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

    easyrsa "build-client-full test nopass"
    

    你正确地得到了错误

    未知命令“build client full test nopass”

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

    您可以将整个命令传递给 系统

    system($cmd)
    

    Perl将把它传递给shell进行处理,就好像是在命令提示下输入的一样。或者可以正确拆分参数

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

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