Reed
Reed

Reputation: 57

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

Bash is not expanding the ~ character in the argument --home_dir=~. For example:

$ echo --home_dir=~
--home_dir=~

Bash does expand ~ when I leave out the hyphens:

$ echo home_dir=~
home_dir=/home/reedwm

Why does Bash have this behavior? This is irritating, as paths with ~ are not expanded when I specify that path as an argument to a command.

Upvotes: 3

Views: 972

Answers (4)

VonC
VonC

Reputation: 1325155

To illustrate chepner's answer, consider Git 2.48 (Q1 2025), batch 15, which describes a case where an option value needs to be spelled as a separate argument, i.e. "--opt val", not "--opt=val".

See commit 1bc1e94 (25 Nov 2024) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit bd31944, 10 Dec 2024)

doc: option value may be separate for valid reasons

Reviewed-by: Eric Sunshine

Even though git help(man) cli recommends users to prefer using "--option=value" over "--option value", there can be reasons why giving them separately is a good idea.

One reason is that shells do not perform tilde expansion for --option=~/path/name but they expand --options ~/path/name just fine.

This is not a problem for many options whose option parsing is properly written using OPT_FILENAME(), because the value given to OPT_FILENAME() is tilde-expanded internally by us, but some commands take a pathname as a mere string, which needs this trick to have the shell help us.

I think the reason we originally decided to recommend the stuck form was because an option that takes an optional value requires you to use it in the stuck form, and it is one less thing for users to worry about if they get into the habit to always use the stuck form.
But we should be discouraging ourselves from adding an option with an optional value in the first place, and we might want to weaken the current recommendation.

In any case, let's describe this one case where it is necessary to use the separate form, with an example.

gitcli now includes in its man page:

Despite the above suggestion, when Arg is a path relative to the home directory of a user, e.g. ~/directory/file or ~u/d/f, you may want to use the separate form, e.g. git foo --file ~/mine, not git foo --file=~/mine.

The shell will expand ~/ in the former to your home directory, but most shells keep the tilde in the latter.
Some of our commands know how to tilde-expand the option value even when given in the stuck form, but not all of them do.

gitcredentials now includes in its man page:

store helper (discouraged) with custom location for the db file;
use --file ~/.git-secret.txt, rather than --file=~/.git-secret.txt, to allow the shell to expand tilde to the home directory.

[credential]
helper = "store --file ~/.git-secret.txt"

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84569

Well, that's because in echo --home_dir=~, the '~' does not begin the word and the output of echo is not considered a variable assignment. Specifically, man bash "Tilde Expansion" provides expansion if

  • If a word begins with an unquoted tilde character (~); or
  • variable assignment is checked for unquoted tilde-prefixes immediately following a : or the first =.

You case doesn't qualify as either.

Upvotes: 2

chepner
chepner

Reputation: 531430

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.)

Upvotes: 3

ilkkachu
ilkkachu

Reputation: 6527

Like chepner says in their answer, according to the documentation, it shouldn't expand it even in echo home_dir=~. But for some reason it does expand it in any word that even looks like an assignment, and has done so at least as far back as in 3.2.

Most other shells also don't expand the tilde except in cases where it really is at the start of the word, so depending on it working might not be such a good idea.

Use "$HOME" instead if you want it to expand, and "~" if you want a literal tilde. E.g.

$ echo "~" --foo="$HOME"
~ --foo=/home/itvirta

(The more complex cases are harder to do manually, but most of the time it's the running user's own home directory one wants.)

Upvotes: 2

Related Questions