env -i zsh | Jak ta magia jest możliwa?

3

Obecnie pracuję w szkole nad domową skorupą, dlatego jestem dzisiaj trochę ciekawy, w jaki sposób ustawiane są warniki muszli. Tak, to może być naprawdę ekscytujące!

W każdym razie oczywiście musimy poradzić sobie z przypadkiem, gdy nasza powłoka zostanie uruchomiona z pustym środowiskiem, tj. Poleceniem „env -i”. Dzięki env -i zmienna zewnętrzna environment jest absolutnie pusta, więc musimy zbudować podstawową ścieżkę, ścieżkę PWD i wszystko.

Naszym referencją jest csh, ponieważ jest to łatwe i proste. Po uruchomieniu csh z env -i, a następnie wydrukowaniu jego zmiennych, oto co możesz zobaczyć:

$ 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, więc jak widać, nie ma zestawu HOME i wydaje mi się to absolutnie normalne. Jeśli wtedy wpiszesz cd, dostaniesz cd: No home directory.. Żyjemy w normalnym świecie.

Ale oto magia. Wpisz wewnątrz pustego pliku csh env -i zsh. zsh się załaduje. Teraz wydrukuj wszystkie zmienne env. (Uwaga: działa idealnie, jeśli piszesz bezpośrednio env -i zsh, nawet z poziomu zsh, robię to tutaj tylko po to, aby mieć całkowitą pewność, że nie ma podstępnej sztuczki z zsh wywoływania siebie czy cokolwiek innego)

% 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

Łał ! Co się stało !? zsh wydaje się (i miał) sposób na odzyskanie $ HOME i $ OLDPWD z tego, co wydaje mi się czystą nicością. (przy okazji, jeśli ktoś nie wie, jestem prawie pewien, że używa paths.h, aby odzyskać swoją ścieżkę).

Oczywiście wiem, że „czysta nicość” nie jest realną możliwością, więc pytam was: Czy wiesz, jak Zsh wraca do HOME i OLDPWD?

Dziękujemy za cierpliwość i życzę miłego dnia! :)

nsierra-
źródło
Czy jesteś w /Users/noesierra-velasqueztrakcie wykonywania tego testu? Prawdopodobnie robi tylko pwd(lub równoważny) i zapełnia $ PWD i $ OLDPWD z bieżącego katalogu. Jeśli chodzi o $ HOME, może to wyglądać w górę (podobnie jak csh robi, gdy się odwołujesz ~scott).
Scott
Dzięki za odpowiedź. Właściwie pisząc to pytanie, pomyślałem, że to może być to! Więc próbowałem manipulować poza rzeczywistym katalogiem domowym ... Wynik? To samo ! Gdziekolwiek to zrobisz, zsh zawsze łapie odpowiedni domowy directoy i oldpwd.
nsierra-
Jeśli chcesz dowiedzieć się, jak zshfaktycznie robi to powłoka, możesz spojrzeć na kod źródłowy zsh.sourceforge.net/Arc/source.html .. na początku może trochę, ale zawsze świetnie jest mieć referencje podczas uczenia się czegoś ekscytujące :)
txtechhelp
Właściwie to czytałem i tak, to ekscytujące! : D Chodzi o to, że nie mamy dużo czasu, a ponieważ nie będziemy wdrażać tej konkretnej funkcji, będę musiał wkrótce przerwać badania z powodu harmonogramu. Dlatego zapytałem społeczność, czy ktoś znał odpowiedź, ale wydaje się, że dla większości z nas jest to tajemnica! :)
nsierra-

Odpowiedzi:

1
  1. Dokumentacja mówi o $HOMEzmiennej:

    HOME <S> Domyślny argument dla polecenia cd. Nie jest to ustawiane automatycznie przez powłokę w emulacji sh, ksh lub csh, ale i tak zwykle jest obecne w środowisku, a jeśli zostanie ustawione, ma swoje zwykłe specjalne zachowanie.

    Przeczytałem go na odwrót, gdy zshjest wywoływany sam, zbiera informacje o homedir z zarządzania użytkownikami systemu. Ten (niekompletny) fragment (z Src/init.c) służy jako dowód:

    / * Uzyskaj hasło i ustaw informacje dla `USERNAME '* /
    if (( pswd = getpwuid (cached_uid))) {
        if (EMULACJA (EMULATE_ZSH))
            home = metafy ( pswd -> katalog_pw, -1, META_DUP);
    ...
    / *
    Wypróbuj tani test, aby zobaczyć, czy możemy zainicjować `PWD 'z` HOME'.
    W emulacjach nienatywnych program HOME musi pochodzić ze środowiska;
    nie możemy ustawić go lokalnie.
    * /
    if (EMULACJA (EMULATE_ZSH))
      ptr = dom ;
    jeszcze
      ptr = zgetenv („HOME”) ;
    ...

    Zatem bez emulacji (tzn. Emuluj zsh), homedir jest określany przez getpwuidwywołanie systemowe. Tylko w innym przypadku $HOMEużywane jest środowisko .

  2. Nie mogę odtworzyć twojej „magii” za pomocą $PWDi $OLDPWD. Ale może powinieneś rozważyć wywołanie zsh as zsh -f, aby żaden użytkownik- (zsh nie zna twojego homedir!) I ustawienia systemowe nie otrzymały źródła. Za pomocą tego polecenia otrzymuję następujące środowisko w oknie debian:

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

    Więc nie ma zaskakujących wartości $PWDi $OLDPWD. Kod źródłowy ustawia te parametry bezpośrednio do powyższego kodu:

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

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

    Dlatego jeśli $HOMEnie jest ustawiony z otoczenia, zgetcwdużywana jest funkcja . Nie wyśledziłem tego, ale założę się, że zwraca bieżący reż ;) . Wreszcie $OLDPWDzostaje zainicjowany z tą samą wartością.

Wszystkie przykłady i fragmenty kodu pochodzą z zsh 5.0.5-dev-0

mpy
źródło
0

Dziękuję mpy za tę cenną informację. Pomogło mi to zrozumieć, jak wszystko działa.

Jednak stworzyłem proste rozwiązanie, aby odzyskać $ HOME, nawet gdy env ma wartość nul (ponieważ było to częścią mojego zadania). Poniższy program działa jak urok i użyj flagi NON-POSIX funkcji glob (3) (więc uważnie sprawdź, czy istnieje w twoim 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);
}

Należy pamiętać, że oryginalnie nieglob(3) jest przeznaczony do tego celu.

nsierra-
źródło