How to get $HOME directory when switching to a different user in bash?

Viewed 217324

I need to execute part of a bash script as a different user, and inside that user's $HOME directory. However, I'm not sure how to determine this variable. Switching to that user and calling $HOME does not provide the correct location:

# running script as root, but switching to a different user...
su - $different_user
echo $HOME
# returns /root/ but should be /home/myuser

Update:

It appears that the issue is with the way that I am trying to switch users in my script:

$different_user=deploy

# create user
useradd -m -s /bin/bash $different_user

echo "Current user: `whoami`"
# Current user: root

echo "Switching user to $different_user"
# Switching user to deploy

su - $different_user
echo "Current user: `whoami`"
# Current user: root
echo "Current user: `id`"
# Current user: uid=0(root) gid=0(root) groups=0(root)

sudo su $different_user
# Current user: root
# Current user: uid=0(root) gid=0(root) groups=0(root)

What is the correct way to switch users and execute commands as a different user in a bash script?

13 Answers

The title of this question is How to get $HOME directory of different user in bash script? and that is what people are coming here from Google to find out.

There is a safe way to do this!

on Linux/BSD/macOS/OSX without sudo or root

user=pi
user_home=$(bash -c "cd ~$(printf %q $USER) && pwd")

NOTE: The reason this is safe is because bash (even versions prior to 4.4) has its own printf function that includes:

%q quote the argument in a way that can be reused as shell input

See: help printf

Compare the how other answers here respond to code injection

# "ls /" is not dangerous so you can try this on your machine
# But, it could just as easily be "sudo rm -rf /*"
$ user="root; ls /"
$ printf "%q" "$user"
root\;\ ls\ /

# This is what you get when you are PROTECTED from code injection
$ user_home=$(bash -c "cd ~$(printf "%q" "$user") && pwd"); echo $user_home
bash: line 0: cd: ~root; ls /: No such file or directory

# This is what you get when you ARE NOT PROTECTED from code injection
$ user_home=$(bash -c "cd ~$user && pwd"); echo $user_home
bin boot dev etc home lib lib64 media mnt ono opt proc root run sbin srv sys tmp usr var /root

$ user_home=$(eval "echo ~$user"); echo $user_home
/root bin boot dev etc home lib lib64 media mnt ono opt proc root run sbin srv sys tmp usr var

on Linux/BSD/macOS/OSX as root

If you are doing this because you are running something as root then you can use the power of sudo:

user=pi
user_home=$(sudo -u $user sh -c 'echo $HOME')

on Linux/BSD (but not modern macOS/OSX) without sudo or root

If not, the you can get it from /etc/passwd. There are already lots of examples of using eval and getent, so I'll give another option:

user=pi
user_home=$(awk -v u="$user" -v FS=':' '$1==u {print $6}' /etc/passwd)

I would really only use that one if I had a bash script with lots of other awk oneliners and no uses of cut. While many people like to "code golf" to use the fewest characters to accomplish a task, I favor "tool golf" because using fewer tools gives your script a smaller "compatibility footprint". Also, it's less man pages for your coworker or future-self to have to read to make sense of it.

The output of getent passwd username can be parsed with a Bash regular expression

OTHER_HOME="$(
  [[ "$(
    getent \
    passwd \
    "${OTHER_USER}"
  )" =~ ([^:]*:){5}([^:]+) ]] \
  && echo "${BASH_REMATCH[2]}"
)"

If you have sudo active, just do:

sudo su - admin -c "echo \$HOME"

NOTE: replace admin for the user you want to get the home directory

Related