Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'm currently working, for school, on a home-made shell, therefore I'm kind of curious nowadays about how shell vars are set. Yes, this can be really exciting !

Anyway, we obviously have to deal with the case when our shell will be launched with an empty environment, i.e. "env -i" command. With env -i, the extern variable environ is absolutely empty, so we have to build ourselves a basic PATH, PWD and everything.

Our reference is csh, cause it's easy and simple. When you launch csh with env -i and then print it's variables, here is what you can see :

$ env -i csh
% set
addsuffix
argv    ()
csubstnonl
cwd /Users/noesierra-velasquez
dirstack    /Users/noesierra-velasquez
echo_style  bsd
edit
gid 20
group   staff
history 100
killring    30
owd
path    (/usr/bin /bin)
prompt  %
prompt2 %R?
prompt3 CORRECT>%R (y|n|e|a)?
shell   /bin/tcsh
shlvl   1
status  0
tcsh    6.17.00
tty ttys001
uid 501
user    noesierra-velasquez
version tcsh 6.17.00 (Astron) 2009-07-10 (x86_64-apple-darwin) options         wide,nls,dl,al,kan,sm,rh,color,filec
% env
HOSTTYPE=intel-mac
VENDOR=apple
OSTYPE=darwin
MACHTYPE=x86_64
SHLVL=1
PWD=/Users/noesierra-velasquez
LOGNAME=noesierra-velasquez
USER=noesierra-velasquez
GROUP=staff
HOST=MacBook-Pro-de-Noe-Sierra-Velasquez.local
REMOTEHOST=

Ok, so as you can see, there is no HOME set and it seems to me absolutely normal. If then you type cd, you'll get cd: No home directory.. We live in a normal world.

But here is the magic. Type, inside your empty csh env -i zsh. zsh will load. Now print all env variables. (Note : it perfectly works if you type directly env -i zsh, even from within zsh, I do it here this way only to be perfectly sure there is no tricky trick with zsh calling himself or whatever)

% env -i zsh
$ env
HOME=/Users/noesierra-velasquez
LOGNAME=noesierra-velasquez
SHLVL=1
PWD=/Users/noesierra-velasquez
OLDPWD=/Users/noesierra-velasquez
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
_=/usr/bin/env

Wow ! What happened !? zsh seems to (and did) have a way to get back $HOME and $OLDPWD from what seems to me to be the pure nothingness. (btw if anyone doesn't know, I'm pretty sure it uses paths.h to get back its path).

Obviously I know that "pure nothingness" is not a viable possibility, so I'm asking you guys : Do you know how zsh is getting back HOME and OLDPWD ?

Thank you for your patience and have a good day ! :)

share|improve this question
    
Are you in /Users/noesierra-velasquez when you do this test? It’s probably just doing a pwd (or equivalent) and populating $PWD and $OLDPWD from the current directory. As for $HOME, it could be looking that up (not unlike what csh does when you refer to ~scott). –  Scott Feb 15 at 2:13
    
Thanks for your reply. Actually, while writing this question, I thought it could be this ! So I tried to do the manipulation outside of the actual home directory... Result ? The same ! From wherever you do it, zsh always catches the right home directoy and oldpwd. –  nsierra- Feb 15 at 2:29
    
If you want to learn how the zsh shell actually does it, you could look at the source code zsh.sourceforge.net/Arc/source.html .. might be a little much at first, but it's always great to have references when learning something exciting :) –  txtechhelp Feb 15 at 9:19
    
I was actually reading it and yes that's exciting ! :D Thing is we don't have much time and since we won't be implementing this particular feature, I'll have to stop my researches soon because of the time schedule. Therefore I asked the community if anyone knew the answer but it seems like it's a mystery for most of us ! :) –  nsierra- Feb 15 at 9:39

2 Answers 2

up vote 1 down vote accepted
  1. The documentation says the following about the $HOME variable:

    HOME <S> The default argument for the cd command. This is not set automatically by the shell in sh, ksh or csh emulation, but it is typically present in the environment anyway, and if it becomes set it has its usual special behaviour.

    I read it the other way round, when zsh is invoked by itself it gathers the informations about the homedir from the user management of the system. This (incomplete) snippet (from Src/init.c) serves as a proof:

    /* Get password entry and set info for `USERNAME' */
    if ((pswd = getpwuid(cached_uid))) {
        if (EMULATION(EMULATE_ZSH))
            home = metafy(pswd->pw_dir, -1, META_DUP);
    ...
    /*
    Try a cheap test to see if we can initialize `PWD' from `HOME'.
    In non-native emulations HOME must come from the environment;
    we're not allowed to set it locally.
    */
    if (EMULATION(EMULATE_ZSH))
      ptr = home;
    else
      ptr = zgetenv("HOME");
    ...

    So, with no emulation (i.e. ,,emulate zsh'') the homedir is determined by the getpwuid system call. Only otherwise, environment's $HOME is used.

  2. I can't reproduce your ,,magic'' with $PWD and $OLDPWD. But perhaps you should consider to invoke zsh as zsh -f, so that no user- (zsh knows your homedir!) and system-wide settings get source'd. With that command I get the following environment on a debian box:

    $ pwd
    /tmp
    $ env -i zsh -f
    jessie% env
    HOME=/home/user
    LOGNAME=user
    SHLVL=1
    PWD=/tmp
    OLDPWD=/tmp
    _=/usr/bin/env
    jessie% 

    So, no surprising values of $PWD and $OLDPWD. The source code setting these parameters follows directly to the above code:

    else if ((ptr = zgetenv("PWD")) && (strlen(ptr) < PATH_MAX) &&
        (ptr = metafy(ptr, -1, META_STATIC), ispwd(ptr)))
        pwd = ztrdup(ptr);
     else {
        pwd = NULL;
        pwd = metafy(zgetcwd(), -1, META_DUP);
     }

    oldpwd = ztrdup(pwd); /* initialize OLDPWD' =PWD' */

    Hence, if $HOME is not set from the environment, a function zgetcwd is used. I didn't tracked this down, but I bet, it returns the current working dir ;). Finally $OLDPWD gets initialized with the same value.

All examples and code snippets are taken from zsh 5.0.5-dev-0

share|improve this answer

Thank you mpy for this precious information. It helped me a lot understanding how everything works.

I however founded a simple solution for getting back $HOME even when env is nul (as it was part of my assignment). Following program works like a charm and use a NON-POSIX flag of the glob(3) function (so be careful checking if it exists in your glob.h).

#include <stdio.h>
#include <glob.h>

int                 main(void)
{
    glob_t          glob_info;
    unsigned int    i;
    int             ret;

    i = 0;
    if (!(ret = glob("~", GLOB_TILDE, NULL, &glob_info)))
    {
        while (i < glob_info.gl_pathc)
            printf("%s\n", glob_info.gl_pathv[i++]);
    }
    else
        printf("Glob error : %d\n", ret);
    return (0);
}

Please note that glob(3) is not made for this purpose originally.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.