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.
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
(and exec
) have four ways of interpreting their arguments, as per the documentation:
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.
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).
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.
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.)
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.)
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.
Solutions
@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: \$? = $?";
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");
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
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.)
Plus, there's the special system(1, @args)
form documented in perlport.