使用多个参数调用 shell 命令 [英] Calling a shell command with multiple arguments

查看:50
本文介绍了使用多个参数调用 shell 命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to automate creating certificates via a Perl script.

我要运行的命令是:

easyrsa build-client-full $clientname nopass

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

The way I thought it should be done in Perl is:

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

然而,这会产生

找不到文件"

关于执行.我三重检查了路径是否正确.

on execution. I triple checked that the path is correct.

如果我这样尝试:

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

Bash 返回

"未知命令 'build-client-full test nopass'.不使用命令运行获取使用帮助."

"Unknown command 'build-client-full test nopass'. Run without commands for usage help."

推荐答案

背景

当您使用 system(LIST)LIST 有多个元素,Perl 不会调用shell,而是直接调用LIST 中第一个元素给出的程序,并使用列表的其余部分作为要逐字传递的命令行参数,shell 没有插值,包括没有在空格上分割参数.

Background

When you use system(LIST) where LIST has more than one element, Perl will not call the shell, and instead directly invoke the program given by the first element in the LIST, and use the rest of the list as command line arguments to be passed verbatim, with no interpolation by the shell, including no splitting arguments on whitespace.

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

So in your first example, Perl is running the command bash and passing the string "$easyrsa_path/easyrsa build-client-full $clientname nopass", literally as one big long argument, and in your second example, it's running the command bash and passing the two arguments $easyrsa_path and "build-client-full $clientname nopass". However, I assume that easyrsa needs the three arguments as separate strings in its argument list, which the shell would normally split, but since both of your calls to system aren't using the shell, it's not working.

system(和 exec) 根据文档有四种解释其参数的方法:

system (and exec) have four ways of interpreting their arguments, as per the documentation:

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

  1. If you pass a single string (including a LIST with only one element) that does not contain any shell metacharacters, it is split into words and passed directly to execvp(3) (meaning it bypasses the shell). Warning: This invocation is easily confused with the following - a single metacharacter will cause the shell to be invoked, which can be dangerous especially when unchecked variables are interpolated into the command string.

如果您传递一个包含 shell 元字符的单个字符串(包括一个只有一个元素的 LIST),则整个参数将传递给系统的命令 shell 进行解析.通常,在 Unix 平台上是 /bin/sh -c,但是 这个想法默认shell"的问题,当然不能保证它会是bash(尽管它可能是).

If you pass a single string (including a LIST with only one element) that does contain shell metacharacters, the entire argument is passed to the system's command shell for parsing. Normally, that's /bin/sh -c on Unix platforms, but the idea of the "default shell" is problematic, and there is certainly no guarantee that it'll be bash (though it could be).

警告:在此 system 调用中,您拥有 shell 的全部功能,这也意味着您有责任正确引用和转义任何 shell 元字符和/或空格.我建议您在您明确希望shell 的强大功能时使用这种形式,否则,通常最好使用以下两种形式之一.

Warning: In this invocation of system, you have the full power of the shell, which also means you're responsible for correctly quoting and escaping any shell metacharacters and/or whitespace. I recommend you only use this form if you explicitly want the power of the shell, and otherwise, it's usually best to use one of the following two.

如果LIST中有多个参数,这会调用execvp(3)LIST中的参数,意思是避免使用外壳.(有关 Windows 的注意事项,请参见下文.)

If there is more than one argument in LIST, this calls execvp(3) with the arguments in LIST, meaning the shell is avoided. (See below for caveats on Windows.)

表单 system {EXPR} LIST always 运行由 EXPR 命名的程序并避免 shell,无论里面是什么列表.(有关 Windows 的注意事项,请参见下文.)

The form system {EXPR} LIST always runs the program named by EXPR and avoids the shell, no matter what's in LIST. (See below for caveats on Windows.)

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

The latter two are desirable if you want to pass special characters that the shell would normally interpret, and I'd actually always recommend doing this, since blindly passing user input into system can open up a security hole - I wrote a longer article about that over on PerlMonks.

@Borodin 和@AnFi 已经指出:如果您只是将 LIST 的元素简单地拆分正确,它应该可以工作 - 看起来您不需要 的任何功能bash 或这里的任何 shell.并且不要忘记检查错误!

@Borodin and @AnFi have already pointed out: If you simply split up the elements of the LIST properly, it should work - it doesn't look like you need any features of bash or any shell here. And don't forget to check for errors!

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

请注意,有一些很好的模块提供了system 的替代方案和 qx,我的首选模块通常是 IPC::Run3.如果您想从外部命令捕获输出,这些模块非常有用.在这种情况下,IPC::System::Simple 可能更容易,因为它提供了具有更好错误处理能力的 system 替代品,以及总是避免 shell 的 systemx.(该模块是 autodie 在您说 时使用的使用 autodie ':all';.)

Note that there are good modules that provide alternatives to system and qx, my go-to module is usually IPC::Run3. These modules are very helpful if you want to capture output from the external command. In this case, IPC::System::Simple might be easier since it provides a drop-in replacement for system with better error handling, as well as systemx which always avoids the shell. (That module is what autodie uses when you say 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$clientname 包含任何 shell 元字符或恶意内容,你最终可能会遇到一个大问题.

Note that if you really wanted to call bash, you'd need to add the -c option and say system("bash","-c","--","$easyrsa_path/easyrsa build-client-full $clientname nopass"). But as I a said above, I strongly recommend against this, since if $easyrsa_path or $clientname contain any shell metacharacters or malicious content, you may end up having a huge problem.

Windows 比上面的更复杂.文档说,避免调用 shell 的唯一可靠"方法是 system PROGRAM LIST 形式,但在 Windows 上,命令行参数不是作为列表传递,而是作为单个大字符串传递,由被调用的命令而不是 shell 来解释该字符串,不同的命令可能会以不同的方式执行此操作 - 另见.(我听说过关于 Win32::ShellQuote 的好消息,不过.)

Windows is more complicated than the above. The documentation says that the only "reliable" way to avoid calling the shell there is the system PROGRAM LIST form, but on Windows, command line arguments are not passed as a list, but a single big string, and it's up to the called command, not the shell, to interpret that string, and different commands may do that differently - see also. (I have heard good things about Win32::ShellQuote, though.)

另外,perlport.

Plus, there's the special system(1, @args) form documented in perlport.

这篇关于使用多个参数调用 shell 命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆