Shell Script Tilde Expansion

How to manually expand a special variable (ex: ~ tilde) in bash

Due to the nature of StackOverflow, I can't just make this answer unaccepted, but in the intervening 5 years since I posted this there have been far better answers than my admittedly rudimentary and pretty bad answer (I was young, don't kill me).

The other solutions in this thread are safer and better solutions. Preferably, I'd go with either of these two:

  • Charle's Duffy's solution
  • Håkon Hægland's solution

Original answer for historic purposes (but please don't use this)

If I'm not mistaken, "~" will not be expanded by a bash script in that manner because it is treated as a literal string "~". You can force expansion via eval like this.

#!/bin/bash

homedir=~
eval homedir=$homedir
echo $homedir # prints home path

Alternatively, just use ${HOME} if you want the user's home directory.

Why is a tilde in a path not expanded in a shell script?

In the bash manual, note that brace expansion during parameter substitution, but not recursively:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion.

This implies that any tilde (or parameter references or command substitution) stored unexpanded in a bash variable will not automatically resolve. Your JAVA_HOME variable contains a literal tilde, so bash will not expand it automatically.

It is likely that your fix worked because tilde expansion does not apply in quotes:

$ echo "~"
~
$ echo ~
/home/jeffbowman

...but parameter expansion like $HOME does occur in quotes. Replacing it with $HOME expands to your home directory during the assignment of JAVA_HOME.

FOO=~/bar        # stores /home/jeffbowman/bar
FOO="~/bar" # stores ~/bar
FOO=$HOME/bar # stores /home/jeffbowman/bar
FOO="$HOME/bar" # stores /home/jeffbowman/bar

Though the better option is to ensure your assignment is correct, if you want to expand it manually, these SO questions have some good options:

  • "Tilde expansion in quotes"
  • "How to manually expand a special variable (ex: ~ tilde) in bash"

Tilde (~/) not working on if then statement in Shell script

Double quotes (really, any quotes) prevent ~ from being expanded. Use instead:

checkfile=~/mysql_backup/"$file"

...or, even better, use $HOME in all scripts, and consider ~ to be a facility for interactive/human use only.

Stop tilde expansion in bash script arguments

It's not under the script's control. It's up to the user. They must quote the tilde to prevent it from being expanded. Options include:

./script.sh \~/docs
./script.sh '~/docs'
./script.sh "~"/docs

Unable to test whether a file exists in Bash

~ is only expanded by the shell if it's unquoted. When it's quoted it's a literal tilde symbol.

local fname=~/.bash_profile

Why use $HOME over ~ (tilde) in a shell script?

Tilde expansion doesn't work in some situations, like in the middle of strings like /foo/bar:~/baz

Bash tilde not expanding in certain arguments, such as --home_dir=~

bash is somewhat mistakenly treating home_dir=~ as an assignment. As such, the ~ is eligible for expansion:

Each variable assignment is checked for unquoted tilde-prefixes immediately following a : or the first =. In these cases, tilde expansion is
also performed.

Since --home_dir is not a valid identifier, that string is not mistaken for an assignment.

Arguably, you have uncovered a bug in bash. (I say arguably, because if you use set -k, then home_dir=~ is an assignment, even though it is after, not before, the command name.)


However, when in doubt, quote a string that is meant to be treated literally whether or not it is subject to any sort of shell processing.

echo '--home_dir=~'

Update: This is intentional, according to the maintainer, to allow assignment-like argument for commands like make to take advantage of tilde-expansion. (And commands like export, which for some reason I was thinking were special because they are builtins, but tilde expansion would have to occur before the actual command is necessarily known.)

Shell Script Tilde Expansion

You will probably need to eval the variable to have it substituted correctly. One example would be to simply do

caminho=`eval "echo $caminho"`

Keep in mind that this will break if caminho contains semicolons or quotes, it will also treat backslashes as escaping, and if the data is untrusted, you need to take care that you're not the target of an injection attack.

Hope that helps.

How to use `~` inside a double quote path reference

A tilde is only expanded if it's unquoted. Quoting (or, equivalent, prepending a backslash) disables expansion and turns them into literal tildes.

It's permissible to begin and end quotes mid-argument. You can quote the spaces while leaving the tilde unquoted. These are all equivalent:

wc -l ~/"file name with spaces.txt"
wc -l ~/'file name with spaces'.txt
wc -l ~/file\ name\ with\ spaces.txt


Related Topics



Leave a reply



Submit