Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Expand Tilde to Home Directory When Reading User Input in Bash

blog/cards/expand-tilde-to-home-directory-when-reading-user-input-in-bash.jpg

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.

Quick Jump: Demo Video

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.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per month (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments