# Rdir: search recursive for files with a given name pattern

**URL:** <https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605>\
**Category:** New to Julia\
**Tags:** question, filesystem\
**Created:** [February 1, 2022, 9:23pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605 "2022-02-01T21:23:19Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 1, 2022, 9:23pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/1 "2022-02-01T21:23:19Z")

</div>

I am looking for a function that lists all files in all sub-directories according to a given search pattern.  
I can imagine that such a function already exist, but I failed to find it.  
Below my version:

```julia
# rdir: list files in all sub-directories that match a given pattern
s_dir = raw"C:\data\julia\scripts\example"
s_pattern = "hl.jl"

# drop files in directories that cannot be reached:
# walkdir(path; onerror = identity)
# https://discourse.julialang.org/t/hello-got-a-quick-question-i-am-using-walkdir-to-scan-the-file-system-for-file/54194

function rdir(s_dir::String, s_pattern::String)
  s_files = String[]
  if ~isdir(s_dir)
    error(string("\"", s_dir, "\" is not a dir!"))
  else
    for (root, dirs, files) in walkdir(s_dir; follow_symlinks = false, onerror = identity)
      # why is this wrong?
      # global s_files
      println("Directories in $root")
      for i_dir in dirs
        println(joinpath(root, i_dir)) # path to directories
      end
      println("Files in $root")
      for i_file in files
        # why is this wrong?
        # global s_files
        if occursin(s_pattern, i_file)
          println(string("Pattern: \"", s_pattern, "\": ", joinpath(root, i_file))) # path to files
          push!(s_files, joinpath(root, i_file))
          println(s_files)
        end
      end
    end
  end
  return s_files
end

s_files = rdir(s_dir, s_pattern)
println("s_files:")
println(s_files)

```

The strange thing is the behavior of the variable `s_files`.  
Why is it not necessary to declare it as global in the for-loop?  
And even more strange, why does it the opposite from what I would  
like to achieve? If I declare them inside the for-loop as global,  
the content is deleted.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [February 1, 2022, 9:31pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/2 "2022-02-01T21:31:58Z")

</div>

[https://github.com/vtjnash/Glob.jl](https://github.com/vtjnash/Glob.jl)

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [February 1, 2022, 9:32pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/3 "2022-02-01T21:32:00Z")

</div>

I think you are after glob patterns?

[https://github.com/vtjnash/Glob.jl](https://github.com/vtjnash/Glob.jl)

The following should do it:

```julia
using Glob

readdir("*.jl", dir)

```

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [February 1, 2022, 9:32pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/4 "2022-02-01T21:32:28Z")

</div>

Oh we wrote at the same time 😃

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 1, 2022, 9:55pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/5 "2022-02-01T21:55:40Z")

</div>

I do not know why, but on my computer it does not work, neither on Linux  
Julia v1.4 nor on MS Windows 10, Julia v1.7.1.  
The error message is in both cases:

> no method matching readdir(::String, ::String)

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [February 1, 2022, 10:05pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/6 "2022-02-01T22:05:55Z")

</div>

I think you need

```julia
readdir(glob"*.jl", dir)

```

but it doesn’t look like Glob.jl acts recursively. So it doesn’t full solve your problem (though it is easy to write a recursive function).

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 1, 2022, 10:20pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/7 "2022-02-01T22:20:04Z")

</div>

Confirmed! 🙂 with `glob"string"` it works.  
And you are right! Unfortunately, it does not work recursively.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [February 1, 2022, 10:21pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/8 "2022-02-01T22:21:08Z")

</div>

Interesting, I remember using it recursively in the past, maybe I implemented the recursion myself? I don’t remember exactly.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [February 1, 2022, 10:23pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/9 "2022-02-01T22:23:20Z")

</div>

You can always provide a glob pattern to look into subdirectories though like `glob"/*/*.jl"` to look into subdirectories if I am not mistaken.

In any case it is worth opening an issue in the repository with a feature request.

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 1, 2022, 10:33pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/10 "2022-02-01T22:33:50Z")

</div>

> [@juliohm](#):
>
> You can always provide a glob pattern to look into subdirectories though like `glob"/*/*.jl"` to look into subdirectories if I am not mistaken.  
> […]

This throws an error on my machine:

> LoadError: Glob pattern cannot be empty or start with a / character

where is the trick? Does `glob"/*/*.jl"` only work as an argument to a function?

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [February 1, 2022, 10:41pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/11 "2022-02-01T22:41:29Z")

</div>

@ellocco please read the README of the package more carefully, I am just typing random snippets of code here from a mobile device. The character / is special apparently and cannot be used at the start of the pattern. Have you tried omitting the character as the error message suggests?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 1, 2022, 10:55pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/12 "2022-02-01T22:55:29Z")

</div>

You could use the Glob package with `walkdir`, for example:

```julia
import Glob
function rdir(dir::AbstractString, pat::Glob.FilenameMatch)
    result = String[]
    for (root, dirs, files) in walkdir(dir)
        append!(result, filter!(f -> occursin(pat, f), joinpath.(root, files)))
    end
    return result
end
rdir(dir::AbstractString, pat::AbstractString) = rdir(dir, Glob.FilenameMatch(pat))

```

Then do e.g. `rdir("somedir", "*.jl")`.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 1, 2022, 10:58pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/13 "2022-02-01T22:58:36Z")

</div>

(It might be useful to add `walkdir` support directly to Glob.jl. In general, it seems much better to have an iterator for this sort of thing, since a recursive directory tree can get huge.)

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [February 2, 2022, 4:19am UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/14 "2022-02-02T04:19:02Z")

</div>

sorry, I was sure Glob is recursive and I was literally thinking `**`, sadly:  
[https://github.com/vtjnash/Glob.jl/issues/19](https://github.com/vtjnash/Glob.jl/issues/19)

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 2, 2022, 6:33am UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/15 "2022-02-02T06:33:33Z")

</div>

There is an enhanced Glob around:  
[Eglob](http://software.clapper.org/Eglob.jl/)  
Unfortunately, the documentation does not make clear to me,  
if I can specify a specific top directory as an input  
parameter to the function, which specifies the starting point  
of the recursive search.

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 2, 2022, 10:08am UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/16 "2022-02-02T10:08:40Z")

</div>

@stevengj Thanks for your code! It is not easy to understand everything, could you be so kind and comment this line:

> [@stevengj](#):
>
> ```julia
> rdir(dir::AbstractString, pat::AbstractString) = rdir(dir, Glob.FilenameMatch(pat))
> 
> ```
> 
> […].

For me it is not easy to understand.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [February 2, 2022, 12:38pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/17 "2022-02-02T12:38:42Z")

</div>

> [@ellocco](#):
>
> could you be so kind and comment this line:
> 
> > [@stevengj](#):
> >
> > `rdir(dir::AbstractString, pat::AbstractString) = rdir(dir, Glob.FilenameMatch(pat))`

I defined [two methods](https://docs.julialang.org/en/v1/manual/methods/) of the `rdir` function. One methodd that takes a `FilenameMatch` pattern, which is defined by the Glob.jl package, can be constructed with `fn"..."`, and is needed by my implementation because that’s what Glob.jl implements `occursin` for. The other method, for convenience, takes a simple string pattern — it is implemented by simply converting your string to a `FilenameMatch` and calling the first method.

This way, you can pass either a string or a `FilenameMatch` to `rdir`. (The latter provides more options, e.g. there is an option to make it case-insensitive.)

This is a pretty common pattern in Julia.

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 2, 2022, 9:03pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/18 "2022-02-02T21:03:43Z")

</div>

> [@stevengj](#):
>
> I defined [two methods](https://docs.julialang.org/en/v1/manual/methods/) of the `rdir` function.  
> […]  
> This is a pretty common pattern in Julia.

Hi Steven,  
thank you for the link to the topic of different methods for one and the same function!  
That link really does widen my horizon!  
Stefan

---

<div class="post-metadata">

**Author:** ![ellocco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ellocco/32/31331_2.png) [@ellocco](https://discourse.julialang.org/u/ellocco)\
**Post date:** [February 9, 2022, 10:04pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/19 "2022-02-09T22:04:44Z")

</div>

The proposed function with the variable type “`FilenameMatch`” in combination with “`occursin`” has the drawback that “`*.lj`” works, but “`string*.jl`” does not. Another option is the variable type “`Glob.GlobMatch`” in combination with “`readdir()`” this enables the usage of the joker char / asterisk “`*`” inside the search string:

```julia
function MyLib_RDir(s_dir::AbstractString, s_pat::Glob.GlobMatch)
    files_filtered = String[]
    for (root, dirs, files) in walkdir(s_dir)
        for i_files in readdir(s_pat, root)
            files_filtered = vcat(files_filtered, i_files)
        end
    end
    return files_filtered
end
# Next: add 2nd method to function "MyLib_RDir"
# https://docs.julialang.org/en/v1/manual/methods/
# purpose: convert "String"-type content into 
# "GlobMatch"-type (defined by the Glob.jl package) pattern,
# by utilizing the first methode of this function
MyLib_RDir(s_dir::AbstractString, s_pat::AbstractString) =
    MyLib_RDir(s_dir, Glob.GlobMatch(s_pat))

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [February 9, 2022, 11:32pm UTC](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605/20 "2022-02-09T23:32:31Z")

</div>

> [@ellocco](#):
>
> The proposed function with the variable type “ `FilenameMatch` ” in combination with “ `occursin` ” has the drawback that “ `*.lj` ” works, but “ `string*.jl` ” doed not

If I am not mistaken, the nice solution proposed by @stevengj will work as per your wishes by doing the minor adjustement:

```julia
append!(result, filter!(f -> occursin(pat, f), files), joinpath.(root, files))

```

[Next page](https://discourse.julialang.org/t/rdir-search-recursive-for-files-with-a-given-name-pattern/75605.md?page=2)
