If you want to store in an array the list of files resulting from the expansion of a glob pattern stored in variable, in bash
, that would be:
pattern='path with wildcard*'
IFS= # disable splitting
shopt -s nullglob # make globs that don't match any file expand to nothing
# instead of the unexpanded pattern.
files=($pattern) # here using split+glob (unquoted expansion in list context)
# with the splitting disabled above.
Or to assign the $files
array directly without using a $pattern
scalar variable:
shopt -s nullglob # make globs that don't match any file expand to nothing
# instead of the unexpanded pattern.
files=('path with wildcard'*)
Then you can test if that list is empty with:
if [ "${#files[@]}" -gt 0 ]; then
echo it did find some files
fi
If you want to check, that among those files, at least one is a regular file (after symlink resolution), you could do:
has_regular_files() {
local file
for file do
[ -f "$file" ] && return
done
false
}
if has_regular_files "${files[@]}"; then
echo there was at least one regular file in the list
fi
To check that it matches only one file and that that file is a regular file:
if [ "${#files[@]}" -eq 1 ] && [ -f "${files[0]}" ]; then
echo one and only one file matches and it is regular.
fi
To check that it matches at least one regular file and that all the matched files are regular:
only_regular_files() {
[ "$#" -gt 0 ] || return
local file
for file do
[ -f "$file" ] || return
done
}
if only_regular_files "${files[@]}"; then
echo at least one regular file and all are regular.
fi
With the zsh
shell, you can use glob qualifiers to match by file type:
if ()(($#)) $~pattern(N-.); then
print at least one regular file in the expansion of the pattern.
fi
- contrary to
bash
, zsh
doesn't do implicit split+glob upon unquoted parameter expansions. Here we're asking for globbing (but not splitting) with $~pattern
.
- We append the
(N-.)
glob qualifier, N
for nullglob
so that if the glob matches no file, it expands to nothing .
to test for regular files only (to the exclusion of any other type of file), -
so that that test be done after symlink resolution so it would also match on files that are symlinks to regular files (like your [ -f "$file" ]
would).
- the expansion of that glob is passed as arguments to an anonymous function,
() {body} args
where the {body}
is (($#))
.
((expr))
is a ksh-style arithmetic expression evaluation operator, that returns true if the expression evaluates to a number other than 0. Here, the expression is $#
, that is a special parameter than expands to the number of positional parameters, in this case the number of arguments of that anonymous function, so the number of files that result from that glob expansion.
binary operator expected
indicates your unquoted$file
brought two words.[ -f … ]
expects exactly one path. If it gets two (so with-f
there are three words inside[ ]
),[
assumes the middle argument is a binary operator (which somehow compares two arguments: its neighbors). If it got even more, the error would betoo many arguments
. The problem is not that*
doesn't expand; the problem is that$file
expands to too many entities. Maybe because there are two matching files; maybe because of word splitting.path with wildcard*
is three words, the last one can expand to more. – Kamil Maciorowski Jun 17 '20 at 04:34-f
test only works on single files. What if the pattern matches a range of filetypes (directories, regular files, named pipes, etc.), should the test fail (because there was at least one file that was not a regular file), or should it succeed (because there was at least one regular file)? – Kusalananda Jun 17 '20 at 06:16. a.sh
looks fora.sh
in$PATH
, not in the current directory (though in the case ofbash
when not insh
mode, it will fall back to using thea.sh
in the current directory if it can't be found in$PATH
). Change to. ./a.sh
if you want to source thea.sh
file in the current directory. – Stéphane Chazelas Jun 17 '20 at 16:23