1

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?

MoonFruit
  • 1,490
  • 1
  • 11
  • 11
  • What are you attempting to match with a glob of `'*'`? Files, a list of something else? Like `cnt=0; for i in *; do myvar="$i"; ((cnt++)); done; [ "$cnt" -ne 1 ] && unset myvar`? Then you can simply test `[ -n "$myvar" ] && echo "only match was '$myvar'"`? – David C. Rankin Aug 22 '19 at 03:48
  • @DavidC.Rankin In this solution, if the glob matches nothing, it will simply have one result that equals to the glob. Hot to solve it? – MoonFruit Aug 22 '19 at 03:54
  • Good catch. `[ "$cnt" -eq 0 ] || [ "$cnt" -ne 1 ] && unset myvar` (see edit) – David C. Rankin Aug 22 '19 at 03:55
  • What is your glob expression? It is just `*` or do you have a specific expression – Inian Aug 22 '19 at 04:33
  • More possibilities are specific expressions. What is the difference between `*` or a specific expression? @Inian – MoonFruit Aug 22 '19 at 04:55
  • No, "$cnt" can't be 0 – MoonFruit Aug 22 '19 at 04:57
  • Can you show us a specific example of what your glob is and what you would like to check. For now, your question is unclear – Inian Aug 22 '19 at 04:59

2 Answers2

5

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
kvantour
  • 25,269
  • 4
  • 47
  • 72
2

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
muru
  • 4,723
  • 1
  • 34
  • 78