The simple answer: "see perldoc -f system" is NOT the solution for several reasons:

A: system will execute "/bin/sh", and not the shell of my choice.

B: system will not terminate the perl-script, like exec does. ( I could fake this with: exit system() )

C: The most important: A shell started with system, does not behave like a login shell: it does not process .profile of the current directory, before executing the command. This is what I really need the shell to do, because that .profile defines the environment for the command to be executed in.

A shell started with system, does not behave like a login shell: it does not process .profile of the current directory

This is not really a Perl question, but depends on the shell. For instance, on some shells, you need to call it with a leading dash ('-') to indicate that it is a login shell. On other shells, you pass certain command line switches. Exactly what is (or what is not) a login shell is not a "magical property" which can be set from the outside, without knowing the shell.

Yes, I know making a shell behave like a loging shell is not standardized. However: the next two Perl-lines do the trick for bash or ksh (the only shells in use at my company). But even so: It would be ok for me if I had to switch for specific shells in the script. I don't really care how I start the shell, just as long as it works.

$shell = "bin/bash"; exec $shell '-';

So: processing the .profile is not the issue, but: doing so AND give a command is something I can do in C, but not with Perl :-( .

I guess that in the end, anything in a .profile could be done in Perl, however: this tool is going to be used by people who have multiple .profile files (and so, a kind of multiple HOME directories) and we want an easy way to switch from one homedir to another. This is what we have been doing for a number of years now, but: the tool that we have now is in C. I'm looking for a way to do the same in Perl. It is not a tool to be used only once, but will be part of a set of developer-tools (for internal use only).

First, to the best I know, Perl's exec pretty directly translates to the system function exec, which you are calling from C, so there should be no difference.

Second, you *can* have bash invoke the profile AND behave like a login shell, just call it like this:

Code

bash --login -c Your command goes here

The difference imposed by -c is NOT login vs. non-login shell, but interactive vs. non-interactive shell. The logic according to which files are sourced, is different from whether -c is used or not used, but this is a different issue from whether it is a login shell.

I just tried your bash command: Created a .profile that just echoes a line: echo "PROFILE"

Maybe your bash is broken, or you are using a very old version? I tried it with bash 3.2, and it worked (in my case, I have it read ~/.bash_profile instead of ~/.profile, but the idea is the same). Maybe you should re-post this problem in a forum dedicated to bash?

No, my bash is not broken (and it is a recent version). It indeed sources ~/.profile, so:the profile in the homedir, where the homedir is the one taken from /etc/passwd. In other words: with this method I cannot source another profile in an arbitrary directory.

It is not really complex: Yes, I understand the basics about unix, since 1990 or so, besides, I'm an experienced programmer, in C though..... So: Yes I do know that a shell would read ~/.profile, but: as I explained: Instead of ~/.profile, I want it to source another .profile, in another directory and, in addition to that execute a command. I do not want to change uid. So I do perfectly know what I want, and know for sure that it is possible in C (I've used the trick for 15 years now). The only thing I'm looking for: How do I do this from Perl?

Yes I do know that a shell would read ~/.profile, but: as I explained: Instead of ~/.profile, I want it to source another .profile, in another directory and, in addition to that execute a command. I do not want to change uid.

OK, this is a more precise specification of your problem now.

Since bash can't have any knowledge about ".profile"'s in foreign directories, it means that we have to tell bash explicitly where the foreign profile is located (AFIK bash has no builtin function telling us the path of some users profile file, given that we have his user id or home directory).

This raises another question: As you certainly know, even a login-bash does not necessarily source your ~/.profile. For example, if ~/.bash_profile exists, that one is sourced instead. Now, do you want to duplicate this logic too, or are you just happy to source just the file ~/.profile, if you find it there?

In any case, once you have found which file you want to source, there are several ways to do this.

Assume that the file you want to source, is ~jim/.profile. One trick to achieve this, goes like this:

Code

bash --noprofile --rcfile ~jim/.profile --norc -i -c ls

Here, we specify the .profile to be sourced by --rcfile, use -i because otherwise the --rcfile would be ignored, and also --noprofile, so that your *own* profile is not sourced.

Invoke this from Perl via system("...") (so that ~jim is correctly resolved), and it should work. But this is not really a Perl question; it's a bash question, and perhaps you will find a more elegant solution in a bash forum.

Suppose I have a $targetdir, with a .profile in it. (or in fact, any other file a login shell would process)

I can start an login, interactive $shell, that will process targetdir/.profile if $command is not set and then waits for input.

If I set $command, the $shell is started (as a login non-interactive shell), it processes the targetdir/.profile, and after that, it executes $command (so: the whole perl-script in fact returns with the return value of $command).

So: no need for extra modules, so I can run this script on any machinethat has a basic perl (which was an other requirement)

It happens to work both for ksh and bash shells (haven't tested others).

As you see, the first argument to the shell depends of wheter or not there is a command to be executed. Don't ask me why, I could not find the reasons for that in the manpages.

BTW: I do know that my style of Perl-coding is more like C, but hey: that's what I'm used to!

As you see, the first argument to the shell depends of wheter or not there is a command to be executed. Don't ask me why, I could not find the reasons for that in the manpages.

Are you refering to "-sh"? In C-terms, this would be argv[0] in your main program, i.e. the name which the child process is supposed to believe it was invoked as. This is not different from the C-version of exec (as I said before, they are essentially the same). bash checks argv[0], and if it starts with a '-', it acts as a login shell.

Actually your code "happens by chance" for ksh too, because ksh seems also to only check the first character. This is risky to generalize, because other shells (zsh, pdksh, scsh, ...) might behave differently. A safe solution would be to have a different invocation for each shell, i.e. for bash:

exec("/usr/bin/bash", "bash", "--login", "-c", $command);

But more seriously, if your chdir-trick really works on your platform, it *really* would mean that your bash is broken, since this is definitely NOT the documented behaviour of bash. You seem to assume that a login bash would execute the profile which it finds in the directory it is started from. This is simply not true for any bash I have encountered so far. If you read the documentation, you will see that bash always gets the profile from the home directory, not from the working directory. In fact, a behaviour like the one you assume, would break many existing scripts.

A better solution would be to set the HOME environment variable to the target directory. At least it works with bash 3.2.49. In Perl, you would do this like this:

$ENV{HOME}=$targetdir; exec "/usr/bin/bash","bash","-c",$command;

The problem is that this isn't documented either. A new version of bash could, without giving special notice, use the information stored in /etc/passwd to get at the home directory. I wouldn't feel comfortable to use such a feature in any productive script...

As you see, the first argument to the shell depends of wheter or not there is a command to be executed. Don't ask me why, I could not find the reasons for that in the manpages.

No, refering to the perl-version, I just experimented a bit: and found out I need to pass "-" as first agrument for interactive shells and "-sh" for non-interactive shells. Only if I do it that way, the shell processes it's profile in both cases (interactive- and non-interactive).

About the ksh stuff: I'm not really sure and guess you are right, I get a headache even if I have to start ksh... For now: I know it works, and has worked this way over the last 15 years, on various linux/solaris systems.

You remark about need to sett HOME first is absolutely right, that is what I do (but forgot to mention). In general: I modify %ENV a lot before calling the sub-shell.

The main reason: we use such sub-shells for building software in a re-producable manner. I need to rely on the fact that the environment in the sub-shell does not depend on environment changes in the ancestor of the shell (so: I do NOT just import %ENV). Some other reasons: I need to inherit terminal-features in my subshell and XAUTHORITHY/SSH for example. We use a specification file, in which we specify the dependencies between the ancestor shell and the sub-shell. This goes beyond the topic of this forum though. It is more something for a SCM-forum.

I just experimented a bit: and found out I need to pass "-" as first agrument for interactive shells and "-sh" for non-interactive shells.

May I ask you why you need to *experiment* here, when all this is clearly documented anyway in the man-page of bash (and quite a few other shells)?

Quote

You remark about need to sett HOME first is absolutely right, that is what I do (but forgot to mention).

Actually, this is what made your trick work. The chdir was unnecessary, as you could have easily found out by yourself: Just set HOME to Jim's home, then chdir to Joe's home, execute a login bash and see who's profile gets sourced.

Quote

I need to rely on the fact that the environment in the sub-shell does not depend on environment changes in the ancestor of the shell

In this case, a login shell isn't that safe either. While you don't get the parent's environment, you get instead everything which some foreign .profile does. A maintenance nightmare! If I would do this, I wouldn't let bash source *anything*, plus call it from an environment which I control.

That is, I would prepare a clean environment, and call a subshell as bash --norc -c COMMAND.