-1

Let's say I generate a two directories with text files in each like so

mkdir "Directory1"
mkdir "Directory2"
touch "Directory1/fileclass1_"{1..5}".txt"
touch "Directory1/fileclass2_"{1..5}".txt"
touch "Directory2/fileclass1_"{1..5}".txt"
touch "Directory2/fileclass2_"{1..5}".txt"

Suppose I verify all the files are inside by doing

A=( "Directory1" "Directory2" )
B=( "fileclass1" "fileclass2" )
for a in "${A[@]}"; do
    for b in "${B[@]}"; do
        for i in {1..5}; do
            name="${a}/${b}*${i}.txt"
            [[ ! -e $name ]] && echo "$name Does Not Exist"
        done
    done
done

This returns

Directory1/fileclass1*1.txt Does Not Exist
Directory1/fileclass1*2.txt Does Not Exist
...

However, if I instead replace the double brackets with singles, I get

A=( "Directory1" "Directory2" )
B=( "fileclass1" "fileclass2" )
for a in "${A[@]}"; do
    for b in "${B[@]}"; do
        for i in {1..5}; do
            name="${a}/${b}*${i}.txt"
            [ ! -e $name ] && echo "$name Does Not Exist"
        done
    done
done

which returns nothing, indicating all files are indeed there.

Why is it in this case, the double brackets fail, while single brackets work? I was under the assumption I should always employ double brackets, is the wildcard in string matching creating something I shouldn't have?

4

The -e conditional operator tests whether a file with the given name exists. It doesn't do any pattern matching. [[ -e fileclass1*1.txt ]] tests whether there is a file called fileclass1*1.txt with a literal asterisk in the name, not whether there is at least one file matching the wildcard pattern fileclass1*1.txt.

With a single bracket [ ! -e $name ], since $name is unquoted, the value of name undergoes wildcard substitution and word splitting. If the value of name is Directory1/fileclass1*1.txt, the effect depends on how many files match the wildcard pattern.

  • If the pattern doesn't match any file, it's left unexpanded. Then the -e operator sees the file name Directory1/fileclass1*1.txt, which doesn't exist, so [ ! -e $name ] is true.
  • If the pattern matches a single file, it's replaced by that file's name. Then the -e operator sees that file name, which exists¹, so [ ! -e $name ] is false.
  • If the pattern matches two or more files, it's replaced by the list of matching file names, which results in a syntax error in the conditional. In this case, [ ! -e $name ] displays an error message and returns a failure status.

The effect is different with [[ -e $name ]] because [[ … ]] is special syntax (whereas [ is an ordinary command whose arguments are expanded normally). No word splitting or wildcard expansion is done inside [[ … ]].

To solve your actual problem, see Test if there are files matching a pattern in order to execute a script

¹ Except in the unlikely case that due to a race condition, the file is removed between the time the shell lists the directory content to expand the wildcard pattern, and the time the -e operator checks for the file's existence.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.