138

I want to obtain home dir of any user with echo

echo ~puchuu
>> /home/puchuu

But I cant use variable

echo ~$USER
>> ~puchuu
echo `echo ~$USER`
>> ~puchuu
Oliver Salzburg
  • 87,539
  • 63
  • 263
  • 308
puchu
  • 1,902
  • Please do not use eval or bash -c with a variable. I added an answer that works safely for an Linux/Unix/macOS system with bash (even if you are not using bash as your shell, it likely has bash available because bashisms are everywhere). https://superuser.com/a/1613980/3376 – Bruno Bronosky Dec 31 '20 at 06:23

7 Answers7

133

You can use eval:

eval echo ~$USER

But see Andrew's comment and glenn's reply below.

choroba
  • 19,261
  • 4
  • 49
  • 52
  • 13
    In bash eval isn't needed it with just echo ~$username it's okay, but in sh eval is needed if is a variable – Felipe Buccioni Nov 25 '15 at 14:23
  • This sometimes gives the wrong value, maybe the home folder of a previous account with the same username? – Andrew Dec 12 '15 at 18:28
  • @AndrewMacFie: What do you mean by "previous"? – choroba Dec 12 '15 at 18:49
  • 1
    @choroba Add a user, delete the user, then add a user with the same username. If the user's home folder is different the second time, this command gives the original home folder rather than the current one. glenn jackman's answer gives the current one. – Andrew Dec 12 '15 at 18:59
  • Note also that if you eval echo "~$USER" you are making the assumption that $USER does not contain special characters that the shell may interpret. For example, if USER="foo\$bar", then when we eval the shell will substitute $bar into your output which is not what you want. Basically, if you take this route, you need to make sure that $USER is sane input. Most of the time it probably will be, but you should bear this in mind. – Zorawar Mar 30 '17 at 01:51
  • if you want to assign it to a variable, use this: userdir=$(eval echo ~$SOMEUSER) – Be Kind To New Users Dec 07 '17 at 03:00
  • 1
    Can confirm that this won't work at all if you have anything other than letters and numbers in a username. The method provided by Evan Carroll & Glen Jackmans answer below appears to work at least on Ubuntu 18. E.g:

    $( getent passwd "john-smith" | cut -d: -f6 )

    – Sk446 Nov 25 '19 at 11:33
  • I can't recreate @Andrew 's claim: I started bash in an ubuntu docker container with docker run -it --rm ubuntu bash and then ran user=foobar; yes $'\n' | adduser --home /first $user; deluser $user; delgroup $user; yes $'\n' | adduser --home /second $user; eval echo ~$user and got /second as the output. – Bruno Bronosky Dec 31 '20 at 06:17
  • 1
    I can't recreate @FelipeAlcacibar 's claim: I started bash in an ubuntu docker container with docker run -it --rm ubuntu bash and then ran username=news; echo ~$username and got ~news I then ran username=news; eval echo ~$username and got /var/spool/news – Bruno Bronosky Dec 31 '20 at 06:19
  • @BrunoBronosky I ignore what base image are you using in the docker, at least on mines uses a sh (BusyBox) shell, check that. – Felipe Buccioni Jan 07 '21 at 14:17
  • The solution of @FelipeBuccioni does not work neither in bash no in sh. It works in zsh though. – Michael F Oct 03 '23 at 06:03
112

This might work for you:

homedir=$( getent passwd "$USER" | cut -d: -f6 )

This will also work on users that are not you. For instance,

homedir=$( getent passwd "someotheruser" | cut -d: -f6 )
Evan Carroll
  • 8,895
glenn jackman
  • 26,306
  • 1
    This is legit, using getenv rather than assuming the location of passwd is even a step further than assuming the location of home is /home/ – Evan Carroll Dec 01 '16 at 22:19
23

It seems you are that user -- why not

echo $HOME

?

  • 8
    This won't work if you are in a sudo'ed environment and did not pass sudo the -H or -i flags - $HOME will still be the previous (sudo'ing) user's home directory. – Asfand Qazi Jun 12 '15 at 11:32
12

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 to how other answers 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.

  • 2
    But "$user" instead of $USER – pihentagy Apr 07 '21 at 11:02
  • cd ... && pwd is not needed, a simple echo will suffice. Also, I suggest to improve quoting: ~$(printf '%q' "$user") – MestreLion Nov 22 '21 at 06:00
  • @MestreLion, will it be user_home=$(bash -c "echo ~$(printf '%q' "$username")") or user_home=$(bash -c "echo ~$username")? Which one is correct? – Kyo Jan 30 '24 at 11:39
  • @Kyo Both are correct and will work if you can make sure $username is an actual user name, but, as explained in the answer, the printf %q version is safer as it protects against code injection if $username comes from untrusted sources – MestreLion Feb 01 '24 at 20:19
3

ZSH users can place the tilde (~) outside the expression. This does not work on Bash:

echo ~`echo $USER`
Giacomo1968
  • 55,001
  • Maybe works, but overly complicated (and archaic with the backticks). Just use echo ~$user or echo ~"$user" if you are afraid of usernames containing "funny" characters. – pihentagy Jan 22 '21 at 13:11
1

Once you login, run cd to go to your home directory, then run pwd to print the working directory.

wjandrea
  • 595
  • That's not going to work; the idea here is to do this in shell script, and presumably without requiring the credentials of the user in question... – SamB Aug 27 '16 at 04:57
  • 1
    -1 for the same reason SamB mentioned. I edited your answer because I want you to see how to use Markdown formatting, and to be more concise. You also missed the much easier method, which is just echo $HOME. – wjandrea Sep 04 '16 at 22:35
-1

how about using realpath instead of eval:

realpath ~$USER

because eval can execute anything, where as realpath will not.

rho
  • 99
  • This doesn't work for me. It just treats ~$USER as a filename in the current directory. For example if USER="bob" and PWD=/home/bob/Desktop, this expands to "/home/bob/Desktop/~bob" – pavon Feb 15 '22 at 23:31
  • ~$USER syntax only work switch the user home, problem is it doesn't expand properly when used in variable and user must be valid. – rho Feb 16 '22 at 11:06
  • 1
    From what I can tell realpath doesn't evaluate the ~bob syntax at all. Rather, in some cases the bash shell expands it and passes the expanded result into realpath. – pavon Feb 16 '22 at 18:15