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.
CodePudding user response:
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.
CodePudding user response:
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.