Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash script -e not detecting filename in a variable

In a BASH script, I'm trying to detect whether a file exists. The filename is in a variable but the -e command seems to be unable to detect the file. The following code always outputs "~/misc/tasks/drupal_backup.sh does not exist"

filename="~/misc/tasks/drupal_backup.sh"

if [ -e "$filename" ]; then
  echo "$filename exists"
else 
  echo "$filename does not exist"
fi

On the other hand, the following code detects the file correctly:

if [ -e ~/misc/tasks/drupal_backup.sh ]; then
  echo "$filename exists"
else 
  echo "$filename does not exist"
fi

Why would this be? How can I get it to detect the file when the filename is in a variable?

like image 208
thornate Avatar asked Jun 28 '10 02:06

thornate


1 Answers

That's an interesting one. Substituting $HOME for ~ works as does removing the quotes from the assignment.

If you put a set -x at the top of that script, you'll see that the version with quotes sets filename to ~/... which is what's given to -e. If you remove the quotes, filename is set to the expanded /home/somebody/.... So in the first case, you see:

+ [ -e ~/... ]

and it doesn't like it. In the second case, you see:

+ [ -e /home/somebody/... ]

and it does work.

If you do it without the variable, you see:

+ [ -e /home/somebody/... ]

and, yes, it works.


After a bit of investigation, I've found that it's actually the order in which bash performs its expansions. From the bash man page:

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

That's why it's not working, the variable is substituted after the tilde expansion. In other words, at the point where bash wants to expand ~, there isn't one. It's only after variable expansion does the word get changed into ~/... and there will be no tilde expansion after that.

One thing you could do is to change your if statement to:

if [[ -e $(eval echo $filename) ]]; then

This will evaluate the $filename argument twice. The first time (with eval), there will be no ~ during the tilde expansion phase but $filename will be changed to ~/... during the variable expansion phase.

Then, on the second evaluation (the one being done as part of the if itself), the ~ will be there during the tilde expansion phase.

I've tested that on my .profile file and it seems to work, I suggest you confirm in your particular case.

like image 109
paxdiablo Avatar answered Nov 01 '22 16:11

paxdiablo