env -i zsh | 这种魔法是如何实现的?

env -i zsh | 这种魔法是如何实现的?

我目前正在为学校做自制的 shell,因此我现在对如何设置 shell 变量有点好奇。是的,这真的很令人兴奋!

无论如何,我们显然必须处理 shell 在空环境中启动的情况,即“env -i”命令。使用 env -i 时,外部变量 environ 绝对为空,因此我们必须自己构建一个基本 PATH、PWD 和所有内容。

我们参考的是 csh,因为它简单易用。当您使用 env -i 启动 csh,然后打印其变量时,您会看到以下内容:

$ 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=

好的,正如您所看到的,没有设置 HOME,这对我来说绝对正常。如果您输入cd,您将得到cd: No home directory.。我们生活在一个正常的世界里。

但这就是魔法所在。在空的 csh 中输入env -i zsh。zsh 将加载。现在打印所有环境变量。(注意:如果你直接输入env -i zsh,即使是在 zsh 内部,它也能完美运行,我在这里这样做只是为了确保 zsh 调用自己或其他什么的没有任何花招)

% 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

哇!发生了什么事!?zsh 似乎(并且确实)有办法从在我看来完全空白的地方找回 $HOME 和 $OLDPWD。(顺便说一句,如果有人不知道,我很确定它使用 path.h 来找回它的路径)。

显然我知道“纯粹的虚无”是不可能的,所以我问你们:您知道 zsh 如何返回 HOME 和 OLDPWD 吗?

感谢您的耐心,祝您有美好的一天!:)

答案1

  1. 文档对该$HOME变量有如下说明:

    HOME <S> cd 命令的默认参数。sh、ksh 或 csh 仿真中的 shell 不会自动设置该参数,但它通常存在于环境中,并且如果设置了该参数,它将具有其通常的特殊行为。

    我反过来读,当它zsh被调用时,它会从系统的用户管理中收集有关主目录的信息。这个(不完整的)片段(来自Src/init.c)可以作为证据:

    /* 获取密码输入并设置“USERNAME”的信息 */
    如果 ((密码=获取 pwuid(缓存的uid))){
        如果(模拟(EMULATE_ZSH))
            = metafy(密码->pw_dir,-1,META_DUP);
    ...
    /*
    尝试一个廉价的测试,看看我们是否可以从“HOME”初始化“PWD”。
    在非本地仿真中,HOME 必须来自环境;
    我们不允许在本地设置它。
    */
    如果(模拟(EMULATE_ZSH))
      指针 =
    别的
      指针 =zgetenv(“主页”)
    ...

    因此,在没有模拟的情况下(即“模拟 zsh”),主目录由系统调用决定。只有在其他情况下,才会使用getpwuid环境。$HOME

  2. 我无法使用$PWD和重现您的“魔法” $OLDPWD。但也许您应该考虑以 调用 zsh zsh -f,这样就不会对用户(zsh 知道您的主目录!)和系统范围的设置进行源化。使用该命令,我在 debian 机器上获得了以下环境:

    $密码
    /tmp
    $ env-i zsh-f
    杰西%环境
    HOME=/主页/用户
    LOGNAME=用户
    低压侧电压=1
    密码=/tmp
    OLDPWD=/tmp
    _=/usr/bin/env
    杰西% 

    因此,$PWD和的值没有什么奇怪的$OLDPWD。设置这些参数的源代码直接遵循上面的代码:

    否则,如果 ((ptr = zgetenv("PWD")) && (strlen(ptr) < PATH_MAX) &&
        (ptr = metafy(ptr,-1,META_STATIC),ispwd(ptr)))
        pwd = ztrdup(ptr);
     别的 {
        密码=空;
        pwd = metafy(获取cwd(),-1,META_DUP);
     }

    oldpwd = ztrdup(pwd); /*初始化OLDPWD' =PWD'*/

    因此,如果$HOME环境中没有设置,则zgetcwd使用函数。我没有追踪这一点,但我敢打赌,它返回当前工作目录 ;).最后$OLDPWD用相同的值进行初始化。

所有示例和代码片段均取自 zsh 5.0.5-dev-0

答案2

感谢 mpy 提供的这些宝贵信息。它帮助我理解了一切是如何运作的。

不过,我发现了一个简单的解决方案,即使 env 为 nul 也能返回 $HOME(因为它是我作业的一部分)。下面的程序运行良好,并使用了 glob(3) 函数的 NON-POSIX 标志(因此请仔细检查它是否存在于您的 中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);
}

glob(3)请注意不是最初就是为此目的而制作的。

相关内容