24

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.

  • 1
    @StephenKitt, no not a duplicate. Here we're talking of the shell behaviour for the home directory of the current user. – Stéphane Chazelas Nov 28 '16 at 15:47
  • In the simple case from /etc/passwd. Some systems can store that information in LDAP, NIS servers, etc. – Satō Katsura Nov 28 '16 at 15:47
  • 1
    Programs (including shells) just call getpwuid(3) or similar. Some systems can be configured to "re-route" getpwuid(3) to retrieve information from /etc/passwd, LDAP, NIS, NIS+ etc. – Satō Katsura Nov 28 '16 at 15:56
  • @StephenKitt, see my answer – Stéphane Chazelas Nov 28 '16 at 16:01
  • 1
    @GAD3R, see discussion above, StephenKitt had already pointed to that same duplicate (which he since retracted), but I argue above (and see also my answer) that it's not a duplicate. – Stéphane Chazelas Nov 28 '16 at 16:18
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 ''
  • 2
    I suppose we can report this issue ! Very nice answer btw, thanks! – LotoLo Nov 28 '16 at 16:17
  • 1
    @LotoLo, yes, I'm busy building with fish's dev git head to see if the bug is there as well. Edit. Yes it is. – Stéphane Chazelas Nov 28 '16 at 16:28
  • Now I'm curious: Which environment variable makes the shell maximally user-unfriendly if unset? PATH? TERM? USER? – user1024 Nov 29 '16 at 0:41
  • There's an odd line here: "would only be an approximation..." The parentheses don't line up in that paragraph, too. What was it supposed to be? – muru Nov 29 '16 at 3:14
  • 1
    @muru Querying the user database ... would only be... not so clear indeed – edc65 Nov 29 '16 at 9:16
6

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).

  • 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.

  • 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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