42

I need to expand a glob pattern (like ../smth*/*, or /etc/cron*/) into a list of files, programmatically. What would be the best way to do it?

2
  • 2
    You don't need to do anything special, just don't quote the *. – Kevin Mar 12 '12 at 14:32
  • 1
    Though if you're going to be trying to parse it, use an array like the answer says. – Kevin Mar 12 '12 at 14:34
54

Just let it expand inside an array declaration's right side:

list=(../smth*/)          # grab the list
echo "${#list[@]}"        # print array length
echo "${list[@]}"         # print array elements
for file in "${list[@]}"; do echo "$file"; done  # loop over the array


Note that the shell option nullglob needs to be set.
It is not set by default.
It prevents an error in case the glob (or one of multiple globs) does not match any name.

Set it in bash with

shopt -s nullglob

or in zsh with

setopt nullglob
6
  • And how do I print that list afterwards? – Rogach Mar 12 '12 at 13:38
  • It is just a regular array. You can do whatever you can with any array. Added some examples. – manatwork Mar 12 '12 at 13:44
  • 1
    There is a problem. If pattern matches no files, it prints itself - which is not very good. – Rogach Mar 12 '12 at 14:21
  • 5
    Ah, shopt -s nullglob solves it. – Rogach Mar 12 '12 at 14:25
  • 1
    @lindhe, unsetopt and the same parameter as used for setopt. – manatwork Dec 5 '15 at 18:48
3

compgen is a Bash built-in that you can pass an escaped(!) pattern to, and it outputs matches, returning true or false based on whether there were any. This is especially useful if you need to pass the glob pattern from a variable/script argument.

glob_pattern='../smth*/*'
while read -r file; do
    # your thing
    echo "read $file"
done < <(compgen -G "$glob_pattern" || true)

adding the || true prevents a false return from compgen causing any problems. This method avoids issues with no matches and does not require changing nullglob options.

2
  • I used to think this was a satisfactory solution, but it turned out not to be. For instance, compgen does not work properly for files with "composed extensions"; i.e. if you have a file file.txt.bin, with two extensions, expanding **/*.bin will weirdly not match them. In my case I was trying to expand the glob in the context of a Git script. Luckily, git ls-files "$glob" works just as I'd expect. While not a general solution outside of Git trees, I thought I might as well point it out here. – resolritter Feb 24 at 11:40
  • @resolritter I just ran compgen -G '/**/*.gz' (on bash v4.2.46) and it found several files with composed extensions. I also have the shell option globstar off. Are you saying compgen -G '**/*.bin' produces no output where echo **/*.bin does? – Walf Feb 25 at 3:26
1

No need to overcomplicate things:

echo your/stuff*
3
  • 2
    this doesn't work. For example: TEST=$(echo your/stuff*) && eval \"$TEST\" will output: your/stuff*: No such file or directory – Sebastian Jun 27 '19 at 17:41
  • 1
    @Sebastian this is a nullglob issue, see above – Alexei Averchenko Jul 4 '19 at 13:52
  • 3
    No, it's not a nullglob issue. Using scape characters is evaluating TEST variable as a string including * and not being expanded. – Sebastian Jul 5 '19 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.