我目前正在为学校做自制的 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
文档对该
$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
我无法使用
$PWD
和重现您的“魔法”$OLDPWD
。但也许您应该考虑以 调用 zshzsh -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)
请注意不是最初就是为此目的而制作的。