Where is the $HOME environment variable set?

I'm looking for the place where $HOME environment variable set. It is after login, to my mind.

I'm using Linux debian 2.6.32-5-686.

5 Answers

How to change it:

(it is defined in /etc/passwd; edit it with usermod -d /some/new/home/dir myusername, though modifying the file may work)


When the home directory is written to the $HOME environment variable:

(the string is passed in as an argument to the login program, which will later set the $HOME env var)


sidenote: When the $HOME variable is not defined, the bash shell will fetch it from its official definition in /etc/passwd and define it in that script. This may indicate that your scripting or programming language may have special treatment for the $HOME variable since it is slightly 'important' (though perhaps not as much as $PATH).

3

On Linux, the HOME environment variable is set by the login program:

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

The login program arranges it before calling exec on your shell (by including it in the arguments to exec), based on the value in /etc/passwd.

1

Edit this by running: usermod -d /home/whatever_dir whatever_user.

Please note that this will (obviously) be the new home directory. Bash will cd to it on login, so make sure it exists and the permissions are correct. In addition, don't forget about .bashrc, .profile, .xinitrc, etc; if they're not in the home directory, they will not be read.

From usermod:

Usage: usermod [options] LOGIN
Options: -c, --comment COMMENT new value of the GECOS field -d, --home HOME_DIR new home directory for the user account -e, --expiredate EXPIRE_DATE set account expiration date to EXPIRE_DATE -f, --inactive INACTIVE set password inactive after expiration to INACTIVE -g, --gid GROUP force use GROUP as new primary group -G, --groups GROUPS new list of supplementary GROUPS -a, --append append the user to the supplemental GROUPS mentioned by the -G option without removing him/her from other groups -h, --help display this help message and exit -l, --login NEW_LOGIN new value of the login name -L, --lock lock the user account -m, --move-home move contents of the home directory to the new location (use only with -d) -o, --non-unique allow using duplicate (non-unique) UID -p, --password PASSWORD use encrypted password for the new password -R, --root CHROOT_DIR directory to chroot into -s, --shell SHELL new login shell for the user account -u, --uid UID new UID for the user account -U, --unlock unlock the user account

I did some digging, and the answer to this is a bit surprising. Take the following test script and chmod +x it:

#!/bin/bash
printf 'My home is: '
echo ~ || echo 'nowhere'

We can run it with ./test.sh and see:

My home is: /home/user

Let's take a peek under the hood with strace.

$ strace ./test.sh |& grep '^open[a-z]*'

openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libtinfo.so.5", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
openat(AT_FDCWD, "./test.sh", O_RDONLY) = 3

I do not see any mention of HOME, rc files, or passwd. Let's try it with a clean env:

env -i bash
echo $HOME #this will be blank since we cleared the env

Nothing, as expected. Let's run the script in the blank env.

env -i bash
./test.sh 

My home is: /home/user

Interesting, the script is able to get home. Now let's trace.

strace ./test.sh |& grep '^open[a-z]*'

Now we see:

openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libtinfo.so.5", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_compat.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_nis.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnsl.so.1", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "./test.sh", O_RDONLY) = 3

I've bolded the interesting lines. As we can see, it would appear that when $HOME is not defined, the shell will try to fill it in, even when not in login or interactive mode.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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

You Might Also Like