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?
7 Answers
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 - getent is the better answer, specially with remote user. in simpler systems ~user should be enough. Modded you up.Rui F Ribeiro– Rui F Ribeiro2015-12-05 17:55:03 +00:00Commented Dec 5, 2015 at 17:55
- @RuiFRibeiro you can't use
~foowith variables inbash. Not directly, anyway.muru– muru2015-12-05 18:24:13 +00:00Commented 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 thegetpwnam()function and so probably that lookup is pretty good. i dont like tab-expansions, though - i like to type tabs.mikeserv– mikeserv2015-12-05 19:27:27 +00:00Commented Dec 5, 2015 at 19:27 - 1Note 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– zwol2015-12-05 21:57:09 +00:00Commented Dec 5, 2015 at 21:57
- 1@HagenvonEitzen Colons are forbidden precisely because they have been used to separate fields.Jenny D– Jenny D2015-12-06 13:14:40 +00:00Commented Dec 6, 2015 at 13:14
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.
- 1No need for eval there. Be careful with your english.Rui F Ribeiro– Rui F Ribeiro2015-12-05 18:02:03 +00:00Commented Dec 5, 2015 at 18:02
- 8@nwk an
evalis needed. Bash doesn't process~fooafter variable expansion.muru– muru2015-12-05 18:23:01 +00:00Commented Dec 5, 2015 at 18:23 - 1Interesting, 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.Rui F Ribeiro– Rui F Ribeiro2015-12-05 18:25:49 +00:00Commented 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– muru2015-12-05 18:34:21 +00:00Commented Dec 5, 2015 at 18:34 - 4Don't use this without verifying that
$USERexpands to just a single string of alphabetic characters.chepner– chepner2015-12-06 02:43:25 +00:00Commented Dec 6, 2015 at 2:43
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).
- 2you 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.Rui F Ribeiro– Rui F Ribeiro2015-12-05 17:57:07 +00:00Commented Dec 5, 2015 at 17:57
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'" 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}":evalis 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/passwddoes not provide a complete representation of users, hence the need forgetent. See read files directly VS getent.Note: See discussion on using
getent passwdhere instead of/etc/passwdin comments. In short, one critical caveat ofgetent passwdis that remote users are often not bulk-listable.getent passwdonly lists local users in many cases, whereasgetent 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 togetent passwdonly 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.
- 2The awk one is trivially fixable - just pipe getent to it instead.muru– muru2023-10-06 23:10:47 +00:00Commented Oct 6, 2023 at 23:10
- 2Note too that
getent passwdisn’t usable everywhere — many directory-based setups prevent listing all the users, in which casegetent passwdonly shows locally-defined users.Stephen Kitt– Stephen Kitt2023-10-09 09:36:23 +00:00Commented 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).Stephen Kitt– Stephen Kitt2023-10-09 09:37:18 +00:00Commented 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 passwdwith no additional parameters only lists local users.Mike Hill– Mike Hill2023-10-09 19:01:32 +00:00Commented Oct 9, 2023 at 19:01
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.
- 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– derobert2019-04-16 17:43:23 +00:00Commented Apr 16, 2019 at 17:43
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 - Link gives "access denied".Greenonline– Greenonline2022-07-28 06:11:52 +00:00Commented Jul 28, 2022 at 6:11
- @kikenchitai your link has nothing to do with
chmodandgetent, could you check whether it’s actually the link you wanted to use?Stephen Kitt– Stephen Kitt2022-07-28 09:38:52 +00:00Commented 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, usegetent 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– Mike Hill2023-10-05 23:36:15 +00:00Commented Oct 5, 2023 at 23:36
$USERthe name of the user that runs the script, or is it an arbitrary username?