我正在尝试通过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'。无命令运行 用于帮助。“
答案 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,它没有用。
如果传递一个不包含任何shell元字符的单个字符串(包括只有一个元素的LIST
),它将被拆分为单词并直接传递给execvp(3)
(意思是它)绕过壳)。
警告:此调用很容易与以下内容混淆 - 单个元字符将导致调用shell,这可能很危险,尤其是当未经检查的变量插入到命令字符串中时。
如果传递包含shell元字符的单个字符串(包括只有一个元素的LIST
),则整个参数将传递给系统的命令shell进行解析。通常,在Unix平台上有/bin/sh -c
,但是the idea of the "default shell" is problematic,并且肯定不能保证它会bash
(尽管可以} em> be)。
警告:在system
的调用中,您拥有shell的全部功能,这也意味着您负责正确引用和转义任何shell元字符和/或空白。如果明确要 shell的强大功能,我建议您仅使用此表单,否则,通常最好使用以下两种方法之一。< / p>
如果LIST
中有多个参数,则使用execvp(3)
中的参数调用LIST
,这意味着避免使用shell。
(有关Windows上的警告,请参阅下文。)
表单system {EXPR} LIST
始终运行EXPR
命名的程序,并避免使用shell,无论LIST
中的内容是什么。
(有关Windows上的警告,请参阅下文。)
如果你想传递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处理的内容,例如输出重定向