26

Each shell has an environment variable $HOME set (ex: /Users/lotolo). If I'm under csh I can unsetenv HOME and still if I do cd I'll be in my home. I've tested this also on bash (unset HOME) and it's the same behavior. So how does the shell know where is my/other_user home? Where does it reads those values?

This is not a duplicate since my question is not how do I know, but how does the shell know HOME. And this behavior is extended to other users as well.

Peter Cordes
  • 6,466
LotoLo
  • 606

3 Answers3

33

In the case of csh and tcsh, it records the value of the $HOME variable at the time the shell was started (in its $home variable as noted by @JdeBP).

If you unset it before starting csh, you'll see something like:

$ (unset HOME; csh -c cd)
cd: No home directory.

For bash (and most other Bourne-like shells), I see a different behaviour than yours.

bash-4.4$ unset HOME; cd
bash: cd: HOME not set

The content of the $HOME variable is initialised by the login process based on information stored in the user database against your user name.

The information about the user name itself is not always available. All a shell can know for sure is the userid of the process that is executing it and several users (with different home directories) can share the same userid.

So, once $HOME is gone there is no reliable way to get it back.

Querying the user database (with getpwxxx() standard API) for the home directory of the first user that has the same uid as the one running the shell would only be an approximation (not to mention the fact that the user database could have changed (or the home directory being defined as a one time value) since the login session started).

zsh is the only shell that I know that does that:

$ env -u HOME ltrace -e getpw\* zsh -c 'cd && pwd'
zsh->getpwuid(1000, 0x496feb, 114, 0x7f9599004697)      = 0x7f95992fddc0
/home/chazelas
+++ exited (status 0) +++

All other shells I tried either complain about that unset HOME or use / as a default home value.

Yet a different behaviour is fish's, which seems to query the database for the user name stored in $USER if any or do a getpwuid() if not:

$ env -u HOME USER=bin ltrace -e getpw\* fish -c 'cd;pwd'
fish->getpwnam("bin")  = 0x7fd2beba3d80
fish: Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory
where the current user has write access.
fish: Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory
where the current user has write access.
--- SIGCHLD (Child exited) ---
/bin
+++ exited (status 0) +++


$ env -u HOME -u USER ltrace -e getpw\* fish -c 'cd;pwd'
fish->getpwuid(1000, 0x7f529eb4fb28, 0x12d8790, 0x7f529e858697) = 0x7f529eb51dc0
fish->getpwnam("chazelas")                                      = 0x7f529eb51d80
--- SIGCHLD (Child exited) ---
--- SIGCHLD (Child exited) ---
/home/chazelas
+++ exited (status 0) +++

SEGV when the user doesn't exist (https://github.com/fish-shell/fish-shell/issues/3599):

$ env -u HOME USER=foo fish -c ''
zsh: segmentation fault  env -u HOME USER=foo fish -c ''
7

So how does the shell know where is my/other_user home?

It doesn't. You're just not performing the experiment properly. As you can see from the C shell manual, the cd command changes to the value of the home variable if supplied with no arguments. If this variable is unset, it doesn't know where to change directory to and prints an error:

machine:~> set home=/
machine:/home/user> cd
machine:~> unset home
machine:/> cd
cd: No home directory
machine:/> 

You unset the wrong variable. It's not HOME, the environment variable, it is home the C shell's internal variable (initialized from the value of the former when the shell starts up, but otherwise an independent variable in its own right).

JdeBP
  • 68,745
  • no on csh (at least on my version: " tcsh 6.18.01 (Astron) 2012-02-14 (x86_64-apple-darwin) ") it's HOME variable carrying home's value. But as @Stephane Chazelas said I must unset the variable before launching the shell as it will set HOME value on start. – LotoLo Nov 28 '16 at 16:10
  • The above is the C shell, which I ran on a handy OpenBSD machine, and demonstrates its behaviour. It's not even the TENEX C shell (albeit that that behaves the same). – JdeBP Nov 28 '16 at 16:15
  • Yes I saw it... I typed csh but apparently it is aliased to tcsh – LotoLo Nov 28 '16 at 16:18
0

The system set the HOME variable at the time of login to be a pathname of the user's home directory. It is set by

  • gdm, kdm or xdm for graphical sessions.
  • login on console, telnet and rlogin sessions
  • sshd for SSH connections

You can change its value but pay attention because .bashrc, .profile, .xinitrc, etc will not be read if they're not in the home directory.

Dababi
  • 3,355
  • 24
  • 23
  • But I can unset it ($HOME)... right? And how does it know where's another user's home? – LotoLo Nov 28 '16 at 15:59
  • 1
    you can update it using the usermod -d HOME_DIR command, when a new usre is created. the default home is /home/$username and it it determined by the login program. – Dababi Nov 28 '16 at 16:14