0

After doing a touch ~/foo.baz, I want to run this simple code in my .bashrc:

bar='~/foo.baz'

echo "$bar"; ls -l "$bar"

if [[ -f ~/foo.baz ]] # This is the old code I want to refactor
then
    echo 'A: It works as expected.'
else
    echo "A: The file $bar is not there."
fi

if [[ -f "$bar" ]] # This should be the 'dynamic' replacement, but doesn't work as I wish.
then
    echo 'B: It works as expected.'
else
    echo "B: The file $bar is not there."
fi

It gives me

~/foo.baz
ls: cannot access '~/foo.baz': No such file or directory
A: It works as expected.
B: The file ~/foo.baz is not there.

Why is this the case? How can i use the [[ -f ... ]] syntax to check for files dynamically?


I already tried to change multiple things, such as [[ ... ]] to [ ... ] or "$bar" to $($bar), $bar, ``$bar` (with one front backtick less, I don't know how to format it) and combinations of them. Non brought any different result. I also looked into this Q&A and tried -e instead of -f among other ideas, none of which solved my issue. bash --version yields

GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)

I expect there to be an answer somewhere, but I couldn't find much that was helpful or even solve my question. If this is a duplicate, please advise on how to look for it instead of just closing or providing a link.

sb813322
  • 129
  • 9
  • I can't reproduce your result. – Jetchisel Dec 23 '22 at 08:20
  • Removing the extra `[` and `]` to support POSIX shell then I ran `dash ./script` Also works as what you expected so, might be something on your end. – Jetchisel Dec 23 '22 at 09:03
  • Sourcing the script also did not change my output. – Jetchisel Dec 23 '22 at 11:38
  • 1
    I can't reproduce your problem either. I'd suggest adding a couple of things before the second `if` to see what's going on: `echo "$bar"; ls -l "$bar"` . It's possible that your shell is not interpreting the `~` the way you're expecting it to: it's not being being expanded to the home dir. – aqn Dec 24 '22 at 21:51
  • 3
    Don't use `~` if you're unsure of how/when it is expanded by the shell. Use `$HOME` instead: just change your first line to `bar=$HOME/foo.baz` or `bar="$HOME/foo.baz"`. If you really want to use tilde (why would you? you already fell in its trap), then your first line should be `bar=~/foo.baz` (with no quotes). – gniourf_gniourf Dec 27 '22 at 08:25
  • 1
    Given your latest edit, the quotes in `bar='~/foo.baz'` will absolutely cause this problem (whether or not `$bar` is quoted later -- it *should* be quoted, but that's not related to this problem). See ["~/Desktop/test.txt: No such file or directory"](https://stackoverflow.com/questions/8409024/desktop-test-txt-no-such-file-or-directory). – Gordon Davisson Dec 27 '22 at 09:15

2 Answers2

0

Learn how to quote properly in shell, it's very important :

"Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[@]}", "a & b". Use 'single quotes' for code or literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. See
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words

$ cat file
bar=~/foo.baz
touch "$bar"

if [[ -f ~/foo.baz ]] 
then
    echo 'A: It works.'
else
    echo "A: The file $bar is not there."
fi

if [[ -f $bar ]] 
then
    echo 'B: It works.'
else
    echo "B: The file $bar is not there."
fi
$ bash file
A: It works.
B: It works.
Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
  • Thank you for the (absolutely justified) hint. However writing `"$bar"` instead of `$bar` didn't change the error. – sb813322 Dec 23 '22 at 11:02
  • @sb813322 Did you replace `bar='~/foo.baz'` by `bar=~/foo.baz` as shown in this answer? – yolenoyer Dec 27 '22 at 08:32
  • @yolenoyer Yes, I did try `bar=~/foo.baz` instead of `bar='~/foo.baz'`. I also provided [a community wiki here](https://stackoverflow.com/a/74937880/16821502) where I summarize all the valuable input i received and some info I got from provided links. – sb813322 Dec 28 '22 at 14:26
0

So I learned a lot and, as yolenoyer commented, the subtle difference of some single quotation marks ' being present or not was the source of my error. They make the ~ not be expanded correctly, aqn already commented pointing into this right direction and gniourf proposed some solutions to that, including

  • bar="$HOME/foo.baz" (probably the way to go)
  • bar=~/foo.baz (without quotes, other traps see below:)

if you ever replace part of this with something with a field separator in it, then it will break. Quoting the values of variables is probably a good thing to get into the habit of anyway.

  • Quoting [the] variable [bar=~/"foo.baz"] will also work but I think that is rather ugly.

(by Michael Hoffman, added formatting/applied info in italic by me)

Please consider this question where I have obtained the quoted answer by Michael Hoffman and more details about tilde expansion. His answer also showed me info "(bash) Tilde Expansion" (along with more information). Sanghyun Lee shows a questionable workaround there: bar=$(eval 'echo ~/foo.baz') (applied by me).

More reading on tilde expansion can be found in this Q/A.


It's also worth mentioning the hints of Gilles Quenot('s answer) about how to quote variables. But "whether or not $bar is quoted later - it should be quoted, but that's not related to this problem" - wasn't the real issue as Gordon Davisson commented. He was also the one pointing to this mentioned Q/A.

sb813322
  • 129
  • 9