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.