Tilde expansion in quotes
I write a script where must find some files in a user-defined directory which may contain tilde (thus, it's possible to have user_defined_directory='~/foo'
). The construct looks like
found_files=$(find "$user_defined_directory" -type f … )
I use quotes to cover possible spaces in that path, but tilde expansion does not work in quotes according to man page. I know about :
operator that probably can do this expansion, but I can’t figure out how to use it here.
The ‘user-defined-directory’ is being taken from another configuration file in user $HOME directory. It is not passing to my script as a parameter, it’s being parsed from that another config in the script I write.
You can use "${user_defined_directory/#~/$HOME}"
to replace a "~" at the beginning of the string with the current user's home directory. Note that this won't handle the ~username/subdir
format, only a plain ~
. If you need to handle the more complex versions, you'll need to write a much more complex converter.
This works given some fairly plausible assumptions, but it is far from obvious code (and isn't a one-liner, either):
# Working function - painful, but can you simplify any of it?
# NB: Assumes that ~user does not expand to a name with double spaces or
# tabs or newlines, etc.
expand_tilde()
{
case "$1" in
(\~) echo "$HOME";;
(\~/*) echo "$HOME/${1#\~/}";;
(\~[^/]*/*) local user=$(eval echo ${1%%/*})
echo "$user/${1#*/}";;
(\~[^/]*) eval echo ${1};;
(*) echo "$1";;
esac
}
# Test cases
name1="~/Documents/over enthusiastic"
name2="~crl/Documents/double spaced"
name3="/work/whiffle/two spaces are better than one"
expand_tilde "$name1"
expand_tilde "$name2"
expand_tilde "$name3"
expand_tilde "~"
expand_tilde "~/"
expand_tilde "~crl"
expand_tilde "~crl/"
# This is illustrative of the 'normal use' of expand_tilde function
x=$(expand_tilde "$name1")
echo "x=[$x]"
When run on my machine (where there is a user crl
), the output is:
/Users/jleffler/Documents/over enthusiastic
/Users/crl/Documents/double spaced
/work/whiffle/two spaces are better than one
/Users/jleffler
/Users/jleffler/
/Users/crl
/Users/crl/
x=[/Users/jleffler/Documents/over enthusiastic]
The function tilde_expansion
deals with the various cases separately and differently. The first clause deals with a value ~
and simply substitutes $HOME
. The second is a case of paranoia: ~/
is mapped to $HOME/
. The third deals with ~/anything
(including an empty 'anything'). The next case deals with ~user
. The catch-all *
deals with everything else.
Note that the code makes the (plausible) assumption that ~user
will not expand to a value containing any double spaces, nor any tabs or newlines (and possibly other space-like characters). If you have to deal with that, life is going to be hell.
Note the answer to chdir()
to home directory, which explains that POSIX requires ~
to expand to the current value of $HOME
, but ~user
expands to the value of the home directory from the password database.
Tilde definitely doesn't expand inside quote. There might be some other bash trick but what I do in this situation is this:
find ~/"$user_defined_directory" -type f
i.e. move starting ~/
outside quotes and keep rest of the path in the quotes.