58

I have an USER variable in my script, and I want to see his HOME path based on the USER variable. How can I do that?

1
  • Is the $USER the name of the user that runs the script, or is it an arbitrary username?
    – Kusalananda
    Commented Oct 19, 2020 at 15:40

7 Answers 7

93

There is a utility which will lookup user information regardless of whether that information is stored in local files such as /etc/passwd or in LDAP or some other method. It's called getent.

In order to get user information out of it, you run getent passwd $USER. You'll get a line back that looks like:

[jenny@sameen ~]$ getent passwd jenny
jenny:*:1001:1001:Jenny Dybedahl:/home/jenny:/usr/local/bin/bash

Now you can simply cut out the home dir from it, e.g. by using cut, like so:

[jenny@sameen ~]$ getent passwd jenny | cut -d: -f6
/home/jenny
6
  • getent is the better answer, specially with remote user. in simpler systems ~user should be enough. Modded you up. Commented Dec 5, 2015 at 17:55
  • @RuiFRibeiro you can't use ~foo with variables in bash. Not directly, anyway.
    – muru
    Commented Dec 5, 2015 at 18:24
  • 1
    @muru - that's true - and is spec'd behavior. the ~ does seem to tab-expand, though, and another spec'd behavior of the tilde-prefix shall be replaced by a pathname of the initial working directory associated with the login name obtained using the getpwnam() function and so probably that lookup is pretty good. i dont like tab-expansions, though - i like to type tabs.
    – mikeserv
    Commented Dec 5, 2015 at 19:27
  • 1
    Note that this only gets you the initial value of $HOME; the user's login scripts are perfectly entitled to change it to something else. This is an unusual thing to do, but I can think of situations where it would be sensible, e.g. choosing between a local and an NFS-mounted homedir.
    – zwol
    Commented Dec 5, 2015 at 21:57
  • 1
    @HagenvonEitzen Colons are forbidden precisely because they have been used to separate fields.
    – Jenny D
    Commented Dec 6, 2015 at 13:14
17

You can use eval to get someone's home directory.

eval echo "~$USER"

At least for local users this works for sure. I don't know if remote users like LDAP are handled with eval.

6
  • 1
    No need for eval there. Be careful with your english. Commented Dec 5, 2015 at 18:02
  • 7
    @nwk an eval is needed. Bash doesn't process ~foo after variable expansion.
    – muru
    Commented Dec 5, 2015 at 18:23
  • 1
    Interesting, thanks, however this only gets the directory of the current user, not of other users. I think LDAP users are not handled, though I can be wrong. Commented Dec 5, 2015 at 18:25
  • 3
    @RuiFRibeiro It will work fine with LDAP, just use whatever variable contains your LDAP user's username instead of USER.
    – muru
    Commented Dec 5, 2015 at 18:34
  • 4
    Don't use this without verifying that $USER expands to just a single string of alphabetic characters.
    – chepner
    Commented Dec 6, 2015 at 2:43
4

The usual place is /home/$USER, but that does not have to be universal. The definitive place to search for such information is inside the file /etc/passwd.

That file is world readable (anyone could read it), so any user has access to its contents.
If the $USER exists in the file, the entry previous to last is the user HOME directory.

This will select the entry and print the HOME directory:

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

For more complex (remote) systems, getent is the usual command to get users information from the NSS (Name Service Switch libraries) system.

A command of

getent passwd "$USER" | cut -d : -f 6

Will provide equivalent information (if available).

1
  • 2
    you have ~user for that, and your solution does not contemplate users in more complex systems, like user coming from LDAP where you have to use getent. Commented Dec 5, 2015 at 17:57
3

If the user doesn't exist, getent will return an error.

Here's a small shell function that doesn't ignore the exit code of getent:

get_home() {
  local result; result="$(getent passwd "$1")" || return
  echo $result | cut -d : -f 6
}

Here's a usage example:

da_home="$(get_home missing_user)" || {
  echo 'User does NOT exist!'; exit 1
}

# Now do something with $da_home
echo "Home directory is: '$da_home'"
1

If you are logged in as root, if you know USER's password, or if the USER has no password, the following is yet another option:

    su -c 'echo ~' ${USER}

Under standard su behavior, if USER is undefined or empty, then su will attempt to run the command as root.

If the value of USER is not a valid user name, then an appropriate error will be raised: su: user <user> does not exist.

There are already plenty of good answers here but this may still help someone.

1
  • This isn't secure, depending on system config. It runs a process (the shell running echo) as that user, so the user could (for example) ptrace the shell and give you arbitrary output. It's also running in the user's setup, so it's possible that the users shell config files are run (which could also give arbitrary outputs). Or environment loads via pam. Etc. Or just do something as simple as send it a SIGSTOP to make it take forever, effectively DOS attacking your script.
    – derobert
    Commented Apr 16, 2019 at 17:43
1

getent is not reliable...

getent will not work as expected for usernames consisting of all numerics.

Consider username of '0029', getent will return system rpc (in my case, rpc may have a different uid on your system) user details and not username 0029 details.

chown and a few other system commands will also have issues... see https://access.redhat.com/solutions/310363

Also see getent man page.

/etc/passwd is readable by all, so try

'awk -F':' -v U=$USER '$1==U {print $6}' /etc/passwd
3
  • Link gives "access denied". Commented Jul 28, 2022 at 6:11
  • @kikenchitai your link has nothing to do with chmod and getent, could you check whether it’s actually the link you wanted to use? Commented Jul 28, 2022 at 9:38
  • I'm also getting "access denied" for the Red Hat page. However, I verified -- @kikenchitai is right (at least in RHEL 9). Manpages are obscure but mention only non-numeric keys are passed to getpwnam(3). As an alternative solution, use getent passwd | grep '^username:' (from stackoverflow.com/q/35909572/1941654). Either way, we're just getting the passwd database and parsing for the home path.
    – Mike Hill
    Commented Oct 5, 2023 at 23:36
1

Several reasonable solutions are already posted, but all have issues. For example:

  • getent passwd "${MY_USER}" | cut -d: -f6: Will fail for numeric usernames, as pointed out by @kikenchitai. getent passwd "<value>" assumes UIDs when given numeric values.

  • eval echo "~${MY_USER}": eval is vulnerable to several security concerns. Additionally, this will fail for usernames with spaces or other interpreted characters.

  • awk -v FS=':' -v user="${MY_USER}" '($1==user) {print $6}' "/etc/passwd": /etc/passwd does not provide a complete representation of users, hence the need for getent. See read files directly VS getent.

    Note: See discussion on using getent passwd here instead of /etc/passwd in comments. In short, one critical caveat of getent passwd is that remote users are often not bulk-listable. getent passwd only lists local users in many cases, whereas getent passwd <UID> also queries remote user databases.

  • su -c 'echo ~' "${MY_USER}": Requires elevated access, among other security concerns (as mentioned by @derobert).

  • getent passwd | grep "^${MY_USER}:": Subject to regex injection. Also subject to getent passwd only querying the local user database in many cases.

This is the most reliable solution I've seen so far:

getent passwd "$(id -u "${MY_USER}")" | cut -d: -f6

Explanation:

  • id -u "${MY_USER}": Get the user's UID. This should reliably work with local and non-local users (e.g., LDAP users via SSSD).
  • getent passwd "<UID>": Get passwd data for the given UID. This will search all user databases via Name Service Switch (not just /etc/passwd).
  • cut -d: -f6: Pull the home directory field. This should be consistent across the majority of systems. Most (all?) distributions both assign the sixth field to the home directory and explicitly reserve colons as delimiters without escaping.
4
  • 2
    The awk one is trivially fixable - just pipe getent to it instead.
    – muru
    Commented Oct 6, 2023 at 23:10
  • 2
    Note too that getent passwd isn’t usable everywhere — many directory-based setups prevent listing all the users, in which case getent passwd only shows locally-defined users. Commented Oct 9, 2023 at 9:36
  • 2
    @muru trivially fixable with the caveat just above (and if that’s fixed by giving the user name on the command line, the caveat mentioned in the first bullet point). Commented Oct 9, 2023 at 9:37
  • Thanks for the discussion @muru and @stephen-kitt -- added some commentary to explain this. I see the same behavior on my hosts, where getent passwd with no additional parameters only lists local users.
    – Mike Hill
    Commented Oct 9, 2023 at 19:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .