-1

I am trying the answer suggested in this question: Test whether a glob has any matches in bash

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

However, I am doing this in a script with variables.

#!/bin/sh
directory=~/nd
glob="*12*.idx"
cd $directory
if test -n "$(find . -maxdepth 1 -name \"$glob\" -print -quit)"
then
    echo found
else
    echo not found
fi

Running the script gives me:

not found

But running the command myself

find . -maxdepth 1 -name "*12*.idx" -print -quit

gives me a result

./notes_archive_2012.idx

Is this a quoting issue?

Community
  • 1
  • 1
Robert Mark Bram
  • 8,104
  • 8
  • 52
  • 73

3 Answers3

1

Yes, it is a quoting issue. The quotes should not be escaped inside $(...). Consider:

test -n "$(find . -maxdepth 1 -name \"$glob\" -print -quit)" # Wrong

In the above form, the argument to -name that find sees starts and ends with a literal double-quote. It would only match file names if the file names themselves began and ended with double-quotes.

The following does what you want:

test -n "$(find . -maxdepth 1 -name "$glob" -print -quit)" # Right

From man bash:

When using the $(command) form, all characters between the parentheses make up the command; none are treated specially.

In other words, if you want a double-quote in a command inside $(...), just use a double-quote. The command inside $(...) should be written just as if it was entered on the command line by itself.

John1024
  • 109,961
  • 14
  • 137
  • 171
0

You can do this without an external command. One way is to abuse the for loop a bit:

# Use the loop as a context for expanding the glob, not actually iterating
# over the results.
for f in $glob; do
    if [[ -f "$f" ]]; then
        echo found
    else
        echo not found
    fi
    break
done

You can also use an array:

files=( $glob )
if [[ -f "${files[0]}" ]]; then
    echo found
else
    echo not found
fi
chepner
  • 497,756
  • 71
  • 530
  • 681
0

find works for local files that exist but not for remote files or any other time the file doesn't exist locally. Sometimes you want to check for an arbitrary match and the pattern is in a string.

bash supports [[ "$string" == shellmatch* ]] and [[ "$string" =~ ^regexmatch.* ]] but does not support any form of [[ "$string" == "$shellmatch" ]] where the match parameter is a string. I haven't found any way to trick bash into treating a string short of writing out a new shell script properly formatted and I'm not convinced spaces or ]]brackets would be correctly supported.

At Rich’s sh (POSIX shell) tricks is a one liner that works with most POSIX shells using no more than case and some clever quoting. Tested to work with bash, sh. Tested to NOT work with zsh.

fnmatch () { case "$2" in $1) return 0 ;; *) return 1 ;; esac ; }

I use this variant so I can get blank to match all

fnmatch () { [ -z "$1" ] && return 0; case "$2" in $1) return 0 ;; esac; return 1 ; }

Examples:

fnmatch 'ab \?def' 'ab ?def'    && echo 'matched' # \ supported
fnmatch 'ab ?def' 'ab cdef'     && echo 'matched' # ? supported
fnmatch 'ab [cd]def' 'ab cdef'  && echo 'matched' # [] supported
fnmatch 'ab {c,d}def' 'ab cdef' && echo 'matched' # case does not support brace expansion
fnmatch 'ab c*' 'ab cdef'       && echo 'matched' # space supported
fnmatch 'ab) c*' 'ab) cdef'     && echo 'matched' # ) supported, does not interfere with case