tags:

views:

218

answers:

6

If I want to check for the existance of a single file, I can test for it using test -e filename or [ -e filename ].

Supposing I have a glob and I want to know whether any files exist whose names match the glob. The glob can match 0 files (in which case I need to do nothing), or it can match 1 or more files (in which case I need to do something). How can I test whether a glob has any matches? (I don't care how many matches there are, and it would be best if I could do this with one if statement and no loops (simply because I find that most readable).

(test -e glob* fails if the glob matches more than one file.)

A: 
#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi
Peter Lyons
This version fails when precisely one file matches, but you can avoid the FOUND=-1 kludge by using the `nullglob` shell option.
Ken Bloom
@Ken: Hmm, I would not call `nullglob` a kludge. Comparing a single result to the original pattern is a kludge (and prone to false results), using `nullglob` is not.
Chris Johnsen
@Chris: I think you misread. I didn't call `nullglob` a kludge.
Ken Bloom
@Ken: Indeed, I did misread. Please accept my apologies for my invalid criticism.
Chris Johnsen
A: 
(ls glob* &>/dev/null && echo Files found) || echo No file found
Damodharan R
+4  A: 
#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi
The MYYN
To avoid a possible false “no matches” set `nullglob` instead of checking to see if a single result equals the pattern itself. Some patterns can match names that are exactly equal to the pattern itself (e.g. `a*b`; but not e.g. `a?b` or `[a]`).
Chris Johnsen
I suppose this fails on the *highly unlikely* chance that there's actually a file named like the glob. (e.g. somebody ran `touch '*py'`), but this does point me in another good direction.
Ken Bloom
Thank you for your input - i adjusted my version.
The MYYN
I like this one as the most general version.
Ken Bloom
+1  A: 
if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

Note that this can be very time cosuming if there are a lot of matches or file access is slow.

Florian Diesch
This will give the wrong answer if a pattern like `[a]` is used when the file `[a]` is present and the file `a` is absent. It will say “found” even though the only file it should match, `a`, is not actually present.
Chris Johnsen
This version should work in an ordinary POSIX /bin/sh (without bashisms), and in the case that I'm need it for, the glob doesn't have brackets anyway, and I don't need to worry about cases that are terribly pathological. But I guess that there is no one good way to do test whether any files match a glob.
Ken Bloom
A: 

To simplify The MYYN's answer somewhat, based on his idea:

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi
Ken Bloom
Close, but what if you are matching `[a]`, have a file named `[a]`, but no file named `a`? I still like `nullglob` for this. Some might view this as pedantic, but we might as well be as fully correct as is reasonable.
Chris Johnsen
A: 

This abomination seems to work:

#!/usr/bin/env bash
set nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

It probably requires bash, not sh.

Ryan Thompson