Which shell does a Perl system() call use?

2020-02-05 18:32发布

问题:

I am using a system call to do some tasks

system('myframework mycode');

but it complains of missing environment variables. Those environment variables are set at my bash shell (from where I run the Perl code).

What am I doing wrong?

Does the system call create a brand new shell (without environment variable settings)? How can I avoid that?

回答1:

It's complicated. Perl does not necessarily invoke a shell. Perldoc says:

If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms, but varies on other platforms). If there are no shell metacharacters in the argument, it is split into words and passed directly to execvp , which is more efficient.

So it actually looks like you would have the arguments passed right to execvp. Furthermore, whether the shell loaded your .bashrc, .profile, or .bash_profile depends on whether the shell is interactive. Likely it isn't, but you can check like this.



回答2:

If you don't want to invoke a shell, call system with a list:

system 'mycommand', 'arg1', '...';
system qw{mycommand arg1 ...};

If you want a specific shell, call it explicitly:

system "/path/to/mysh -c 'mycommand arg1 ...'";


回答3:

I think it's not the question of shell choice, since environment variables are always inherited by subprocesses unless cleaned up explicitly. Are you sure you have exported your variables? This will work:

$ A=5 perl -e 'system(q{echo $A});'
5
$

This will work too:

$ export A=5
$ perl -e 'system(q{echo $A});'
5
$

This wouldn't:

$ A=5
$ perl -e 'system(q{echo $A});'

$


回答4:

system() calls /bin/sh as a shell. If you are on a somewhat different box like ARM it would be good to read the man page for the exec family of calls -- default behavior. You can invoke your .profile if you need to, since system() takes a command

system(" . myhome/me/.profile && /path/to/mycommand")


回答5:

I've struggled for 2 days working on this. In my case, environment variables were correctly set under linux but not cygwin.

From mkb's answer I thought to check out man perlrun and it mentions a variable called PERL5SHELL (specific to the Win32 port). The following then solved the problem:

$ENV{PERL5SHELL} = "sh";

As is often the case - all I can really say is "it works for me", although the documentation does imply that this might be a sensible solution:

May be set to an alternative shell that perl must use internally for executing "backtick" commands or system().

If the shell used by perl does not implicitly inherit the environment variables then they will not be set for you.



回答6:

I messed with environment variables being set for my script on this post where I needed the env variable $DBUS_SESSION_BUS_ADDRESS to be set, but it wouldn't when I called the script as root. You can read through that, but in the end you can check whether %ENV contains your needed variables and if not add them.

From perlvar

   %ENV
   $ENV{expr}
           The hash %ENV contains your current environment.  Setting a value in "ENV" changes
           the environment for any child processes you subsequently fork() off.

My problem was that I was running the script under sudo and that didn't preserve all my user's env variables, are you running the script under sudo or as some other user, say www-data (apache)?

Simple test:

user@host:~$ perl -e 'print $ENV{q/MY_ENV_VARIABLE/} . "\n"'

and if that doesn't work then you will need to add it to %ENV at the top of your script.



回答7:

try system("echo \$SHELL"); on your system.