3

I am trying to check wether a file exists or not: This works as expected:

if [ -e "/tmp/test.*.lock" ]; then
  echo "exists!"
fi

This does not:

if [[ -e /tmp/test.*.lock ]]; then
  echo "exists!"
fi

The filename could be something like /tmp/test.123.lock

I could not find a way to make it work with the "[[" version. Can someone explain me why and how to make it work?

AlexD
  • 78
  • Your first command will result in an error if more than one file matches the pattern and so will the second. The problem is that you need to make sure that one and only one filename is being tested for, as shell expansion will make a pseudofilename as concatenation of matching names with spaces (e.g. a file called `test.123.lock\ test.456.lock') and thus fail. Think about how to select the specific file. – FelixJN Oct 21 '19 at 14:52
  • Thank you for the hint, I will rethink my approach on doing this. Still I ask myself why it works with the single "[" if there is only one matching file. – AlexD Oct 21 '19 at 15:06
  • @Freddy but that's just a separate question, really. And one that is better answered outside the context of the title question here. E.g. https://unix.stackexchange.com/q/32210/135943, https://unix.stackexchange.com/q/99185/135943, https://unix.stackexchange.com/q/306111/135943 – Wildcard Oct 21 '19 at 18:06
  • @Wildcard Agreed, there are enough other answers covering that topic, although I haven't found one that covers pathname expansion in detail. I can't retract my reopen vote, I guess it's too late now. – Freddy Oct 21 '19 at 18:28

2 Answers2

5

User xenoid us right, pathname expansion does not occur in [[ expressions.

From the bash man page:

[[ expression ]]
Return a status of 0 or 1 depending on the evaluation of the conditional expression expression. Expressions are composed of the primaries described below under CONDITIONAL EXPRESSIONS. Word splitting and pathname expansion are not performed on the words between the [[ and ]]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed. Conditional operators such as -f must be unquoted to be recognized as primaries.

(emphasis mine)

To use the [[ test, you need to do the expansion before the actual test. This could be done with the help of an array to store the generated filenames.

Below are two examples: The first tests for one matching file and the the second for at least one matching file and breaks out of the loop if a match was found.

files=( /tmp/test.*.lock )

# test if exactly one file matches
if [[ "${#files[@]}" -eq 1 ]] && [[ -e "${files[0]}" ]]; then
  echo "file ${files[0]} exists!"
fi

# test if at least one file matches
for f in "${files[@]}"; do
  if [[ -e "$f" ]]; then
    echo "at least one file exists!"
    break
  fi
done

Note that you don't need quotes around variables in [[ expressions, but it doesn't hurt to do so and you could replace [[ with [. If you know that your lock file is a regular file, use -f instead.

Freddy
  • 25,565
1

[ is a command (actually a shell builtin which is an optimization of /usr/bin/[, as is done for other commands such as echo and printf), so whatever follows is subject to filename expansion like with any command.

[[ is a bash keyword. It appears file expansion isn't performed inside the brackets (but I cannot find anything about this in the bash reference manual).

xenoid
  • 8,888