Test whether a glob has any matches in bash

2019-01-01 15:03发布

If I want to check for the existence 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.)

标签: bash glob
18条回答
还给你的自由
2楼-- · 2019-01-01 15:20
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.

查看更多
无与为乐者.
3楼-- · 2019-01-01 15:21
#!/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
查看更多
流年柔荑漫光年
4楼-- · 2019-01-01 15:22

The nullglob shell option is indeed a bashism.

To avoid the need for a tedious save and restore of the nullglob state, I'd only set it inside the subshell that expands the glob:

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

For better portability and more flexible globbing, use find:

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

Explicit -print -quit actions are used for find instead of the default implicit -print action so that find will quit as soon as it finds the first file matching the search criteria. Where lots of files match, this should run much faster than echo glob* or ls glob* and it also avoids the possibility of overstuffing the expanded command line (some shells have a 4K length limit).

If find feels like overkill and the number of files likely to match is small, use stat:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi
查看更多
像晚风撩人
5楼-- · 2019-01-01 15:23
(ls glob* &>/dev/null && echo Files found) || echo No file found
查看更多
查无此人
6楼-- · 2019-01-01 15:25

I like

exists() {
    [ -e "$1" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

This is both readable and efficient (unless there are a huge number of files).
The main drawback is that it's much more subtle than it looks, and I sometimes feel compelled to add a long comment.
If there's a match, "glob*" is expanded by the shell and all the matches are passed to exists(), which checks the first one and ignores the rest.
If there's no match, "glob*" is passed to exists() and found not to exist there either.

Edit: there may be a false positive, see comment

查看更多
步步皆殇っ
7楼-- · 2019-01-01 15:26

I have yet another solution:

if [ "$(echo glob*)" != 'glob*' ]

This works nicely for me. Are there some corner cases I miss?

查看更多
登录 后发表回答