MoonFruit
MoonFruit

Reputation: 1660

Test whether a glob has only one match in bash and get it

The question "Test whether a glob has any matches in bash" and it's answer is quite well.

But, I want to know how to test whether a glob has only one match in bash and if it exists assign to a variable.

How can I do it?

Upvotes: 1

Views: 991

Answers (2)

kvantour
kvantour

Reputation: 26481

You do not need nullglob. With a simple test you can validate if your glob expanded to a single entry, or zero or more entries:

globexpand=( globexpression )
[[ -e "${globexpand[@]}" ]] && var="$globexpand"

This works for the following reasons:

  1. if globexpression matches multiple files, the test with -e will fail
  2. if globexpression matches a single file, the test with -e will match
  3. if globexpression matches no file, the globexpand will hold the globexpression as a single entry and will fail the test with -e.

So you do not need any nullglob or any special option. Just make sure you quote correctly here to handle filenames with special characters.

An alternative way is to make use of find to count how many matches your glob has:

$ find . -maxdepth 1 -name 'globexpr' -printf c | wc -c

We make use of printf so that we do not have a problem with funny filenames which might contain a newline character. Having this said, it is now straightforward:

if [[ $(find . -maxdepth 1 -name 'globexpr' -printf c | wc -c) == "1" ]]; then
    # do your magic
else
    # do some other stuff
fi

Upvotes: 6

muru
muru

Reputation: 4897

You can set nullglob to expand globs to nothing if they match nothing. That will solve the problem the glob hanging around if there are no files matching it.

For example, here's a function which sets nullglob only for the function:

set_one () {
    # Usage:
    #    set_one myvar "some?glob*"

    # save nullglob setting (https://stackoverflow.com/a/34957289/2072269),
    # disable field splitting and enable nullgob
    declare nullglob=$(shopt -p nullglob) IFS=
    shopt -s nullglob

    # expand second argument as a glob and reset nullglob to saved setting
    declare -a array=($2) 
    eval "$nullglob"

    (( ${#array[@]} != 1 )) && return 1

    # save first element of array to variable given by first argument
    printf -v "$1" "%s" "$array"
}

Example:

$ ls
a  b  c
$ set_one foo "*"; echo $? "$foo"
1
$ set_one foo "a*"; echo $? "$foo"
0 a
$ set_one foo "[d]"; echo $? "$foo"
1 a  # retains previous value

Upvotes: 2

Related Questions