Expand Tilde to Home Directory When Reading User Input in Bash
A common use case is to ask a user to define a path using a tilde but you want that to really be their home directory path.
Imagine you’re asking a user to provide a path to where you’ll install something to. You may choose to do something like this:
read -rp "Where should XYZ get installed to? [~/demo] " path
If they don’t supply a path it will get installed to ~/demo
but you want ~
to be expanded to ${HOME}
and not be a literal ~
character as part of their
path.
For that you can do: path="${path/#~/${HOME}}"
That will use Bash parameter expansion to replace ~
with the user’s real home directory only if the first character is ~
. The
first character rule is defined by #
.
Here’s a full example you can run:
#!/usr/bin/env bash
set -o errexit
set -o pipefail
set -o nounset
read -rp "Where should XYZ get installed to? [~/demo] " path
# Here's where we do the string replace.
path="${path/#~/${HOME}}"
# If there's no path supplied then use the default.
[[ -z "${path}" ]] && path="${HOME}/demo"
mkdir -p "${path}"
touch "${path}/hello"
printf "\nXYZ has been installed to: %s\n" "${path}"
$ ./demo
Where should XYZ get installed to? [~/demo]
XYZ has been installed to: /home/nick/demo
You can also pass in a custom path:
$ ./demo
Where should XYZ get installed to? [~/demo] ~/cool/story
XYZ has been installed to: /home/nick/cool/story
However, if you pass in:
$ ./demo
Where should XYZ get installed to? [~/demo] hello/~/world
XYZ has been installed to: hello/~/world
Now you have a literal hello/~/world
directory relative to where you ran the
demo
script. That’s because our path didn’t start with ~
so the #
rule
kicked in.
If you want all instances of ~
to be replaced even if the path didn’t start
with ~
then you can do path="${path/\~/${HOME}}"
. All we did was swap #
with \
to escape the ~
.
Now you can run:
$ ./demo
Where should XYZ get installed to? [~/demo] hello~/world
XYZ has been installed to: hello/home/nick/world
If you ran the script with the default value it still produces
/home/nick/demo
too since it replaces the first ~
with ${HOME}
.
Now you have both options at your disposal.
Personally I tend to roll with the #~
approach for creating paths since you’d
typically start your path with ~
if you wanted something in your home
directory. If a user had a whacky use case where they wanted a literal relative
~
path they could do it.
# Demo Video
Timestamps
- 0:34 – Running the script to see how it works
- 1:22 – Only replacing the tilde if it’s the first character
- 2:08 – Replacing all instances of the tilde with the home directory
- 3:12 – It’s up to you on which one to pick
What have you used this for in the past? Let me know below.