3

I'm sure this has been asked before but I can't find anything. We have inscrutable login names on a shared machine and want to use shell variables to substitute the hard-to-remember login names for people's real names.

For example, let's say Omar's login name is xyz123. I can do this:

$ omar=xyz123
$ echo ~$omar

and output looks fine:

~xyz123

but if I type this:

$ ls ~$omar

there is an error:

ls: cannot access ~xyz123: No such file or directory

I think it's because tilde expansion happens before variable expansion but can't figure out how to get around this.

Perhaps this answer is related although I'm not sure: How to manually expand a special variable (ex: ~ tilde) in bash

JohnE
  • 29,156
  • 8
  • 79
  • 109
  • For reference: http://wiki.bash-hackers.org/syntax/expansion/tilde – bishop Nov 30 '18 at 21:02
  • The output looks "fine" `~xyz123` but it is not the path to `xyz123` home directory. – KamilCuk Nov 30 '18 at 21:05
  • 2
    None of the possible workarounds are very attractive. Muttering terms of abuse such as `eval` won't endear me to the security folks — and there's no blame to them for that. – Jonathan Leffler Nov 30 '18 at 21:09
  • There's a lot of overlap between this and [How to manually expand a special variable (ex: ~ tilde) in bash](https://stackoverflow.com/questions/3963716/how-to-manually-expand-a-special-variable-ex-tilde-in-bash); some answers, such as https://stackoverflow.com/a/30770111/14122 and https://stackoverflow.com/a/29310477/14122, are directly on-point. – Charles Duffy Nov 30 '18 at 21:12
  • 1
    Taking a step *way* back, I'd say it's rare to need someone *else's* home directory, which isn't necessarily accessible by someone else anyway. Either the script will be run by `omar`, in which case you'll just use `~`, or the script will be run as root, who can just use `su` to become `omar` as necessary. – chepner Nov 30 '18 at 21:13
  • 1
    You might want to use the `cdable_vars` option instead; add a variable like `omarhome=/home/omar` (look it up manually once and store the value in the variable), then `cd omarhome` will be equivalent to `cd "$omarhome"`. (Other commands, of course, will have to use the variable more explicitly, e.g. `ls "$omarhome"`.) – chepner Nov 30 '18 at 21:23
  • @chepner It's not rare on shared machines where you work closely with others and is why the syntax `~other_user` exists in addition to plain `~` (well, I assume that's why it exists) – JohnE Nov 30 '18 at 21:30
  • @chepner yeah, just writing out the path (w/ or w/o cdable_vars) is perhaps the most obvious and simplest way. Maybe the best too ;-) – JohnE Nov 30 '18 at 21:46

2 Answers2

3

bash expands the tilde before the variable. See https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions

The shell will see if the literal characters $ o m a r are a login name. As they are not, the tilde is not expanded. The shell eventually sees $omar as a variable and substitutes that. It then hands the expanded word ~xyz123 to echo which just prints it.

Similarly, it hands the word ~xyz123 to ls. Since ls does not do its own tilde expansion, it is looking for a file in your current directory named ~xyz123 with a literal tilde. Since such a file does not exist you get that error.

If you want ls ~$var to list files, you need eval ls ~$var. Or, since eval is considered very unsafe to use casually, you could do this instead:

ls "$(getent passwd "$omar" | cut -d: -f6)"
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 1
    eg `home() { getent passwd ${1:?Missing user name} | cut -f6 -d:; }` followed by -- eg `ls "$(home $omar)"`. Not as convenient as `~` certainly, but safest. – bishop Nov 30 '18 at 21:11
0

I would go with checking if "$omar" is a valid user with id and then using eval to force double expansion. So protect against evil eval and then do it.

if ! id "$omar" >/dev/null 2>&1; 
   echo "Error: user with the name $omar does not exist!" >&2
   exit 1
fi
eval echo "\"~$omar\""
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
KamilCuk
  • 120,984
  • 8
  • 59
  • 111