BBQ has asked for the wisdom of the Perl Monks concerning the following question:

I guess this question would be better fit for sysadminmonks.org, but here we go anyway... :)

I am setting up a machine that will contain a general profile for a specific group of users. Whenever a user in that group logs on via telnet or on console, the profile is executed containing a perl script that goes through specific tasks per user, but I'm not worried about that yet. There's another smaller issue that's bothering me.

At the end of the script, I'm trying to chdir and have the user stay at that directory, but the common shell behavior won't set the user's cwd to that directory. Instead, he starts off at his/her home directory. In other words:
foo:~ # pwd /home/foo foo:~ # perl -e "chdir('/bar');" foo:~ # pwd /home/foo
Q: Does anyone know how to avoid / work around this shell behavior?

Thans in advance.

Replies are listed 'Best First'.
Re: chdir vs. shell behavior
by gnat (Beadle) on Jun 14, 2000 at 22:31 UTC
    Your shell has to execute the cd. So have the .login or whatever else is sourced by the shell say:
    eval `theperlconfigstuff`
    
    and have theperlconfigstuff print "cd /path/to/be/in"
RE: chdir vs. shell behavior
by merlyn (Sage) on Jun 14, 2000 at 21:24 UTC
    I believe this is a FAQ, and if it isn't, it should be. per-process features, like current directory are not inherited upward, only downward. There's nothing any child process can do that can affect the parent's directory, without the parent cooperating in very unusual ways.

    -- Randal L. Schwartz, Perl hacker

      Yup. The FAQ (perlfaq8) says:

      In the strictest sense, it can't be done -- the script executes as a different process from the shell it was started from. Changes to a process are not reflected in its parent, only in its own children created after the change. There is shell magic that may allow you to fake it by eval()ing the script's output in your shell; check out the comp.unix.questions FAQ for details.

      "There is shell magic that may allow you to fake it" is exactly what I'm after. At this point, I'm not worried about portability, compatibility or downright correctness. If it gets the job done, I'm game.

      #!/home/bbq/bin/perl
      # Trust no1!
        On the one occasion I had cause to do this, I exec'd the perl script from the login script, and exec'd a new shell from the perl script. Problem was, that launched the shell twice - easily solved in my case, as I had the luxury of making the perl script the login shell for the relevant users. This also solves the problem of needing to stop the script from executing twice (either by assuming a login shell the first time around, or setting an environment variable from within the script)

        As someone else pointed out, passing the output from the script to a cd command works too. But things complicate if you want to change more of the environment than just the directory...

        That is (shell script):

        # ... stuff run every time this script is invoked if [ "$checkvar" != "foo" ]; then exec perl /usr/script/myscript.pl else # ... stuff to do after the script has run fi
        And in the perl script:
        $ENV{checkvar}="foo"; exec "sh", "/usr/script/myshell.sh" # or, if you're logging in exec "sh", "-l"
        There may be a problem if the shell script is not being executed as part of a login sequence, as you can't assume the environment is trustworthy. I suspect in most cases all someone who fakes the variable is going to do is make the perl script fail to run...
Re: chdir vs. shell behavior
by Anonymous Monk on Jun 15, 2000 at 02:03 UTC
    In case you are not quite there yet: have .profile (or /etc/profile) exec the perl script have the perl script end by execing the shell with a param that invokes an rcfile that, finally, cd's you to the appropriate dir (e.g. "/bin/bash -rcfile /etc/cdrc")
RE: chdir vs. shell behavior
by PipTigger (Hermit) on Jun 15, 2000 at 14:03 UTC
    I'd really love to understand how to do this as well. The supplied answers suggest .profile modifications but this only solves login initialization before the "real" shell is invoked (right?). I've been longing to port lots of my old 4Dos batch files to Perl but they won't affect my current shell environment. I don't want to write shell scripts. I'm just not a "fi" kinda guy. I have an script "mc" which takes one param and does a mkdir && a chdir on it which I find very useful... in 4Dos but Perl can't do it. Even if it's convoluted or compromises security or whatever, doesn't anybody know how to manipulate your parent process effectively from a Perl script? If I ever figure out on my own, I'll be sure to post it but all I've ever heard is it can't be done and I haven't had time to try to be underhanded myself yet. Hopefully somebody knows. TTFN & Shalom.

    -PipTigger

    p.s. Byslexia is a Ditch!
      It is a FAQ anyway, but I'll explain and risk setting a precedent...

      DOS does not have anything remotely like the Unix concept of a process. When a piece of software in DOS launches another one, it loads it into its own memory space and continues from that programs start point. This means that all programs under DOS share the same environment, including directory, environment variables, and so on.

      In Unix and workalikes, there is the concept of processes. These have their own environment, and usually have a parent process, which their initial environment is inherited from. This means that when you start a new process with system() or whatever, it gets a copy of CSD and environment variables of the process that started it. When it alters its environment, it only affects itself and any child processes it launches after that point. So, if you do a chdir from a process, it only affects that process's environment. If you consider that under Unix that the processes could be running in parallel, you should realise that this is a Good Thing.

      However, this means that a child process cannot directly manipulate its parents environment. It needs to communicate with the parent process - easiest way being backticks, supported in both perl and shell scripts. IE:

      eval `myscript.pl`
      Would cause the output of myscript to be evaluated in the process that launched it.

      Next up - the meaning of exec()! exec() is the closest thing Unix has to DOS's model of execution - it replaces the program running in the current process with a new one, without creating a new process. The old program is never re-entered, though - if you want to do that, you have to do it yourself (the design of most modern Unixen makes this less costly than most people think). A lot of beginners make the mistake of using system() where exec() would be more appropriate (system() does a fork() and then an exec() under Unix. Actually, it also waits for the child process to terminate). Use exec() if you've finished with the current process.

      Andrew (mysteries explained, or at least made more mysterious)