8.5.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.0.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.1.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.2.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.3.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.4.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.6.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
4.6.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
4.7.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
5.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
6.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
7.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)

Finds all files that match a given mask in a given directory.

Directories and files beginning with a period are excluded; this prevents hidden files and directories (such as SVN working directories) from being scanned.


$dir: The base directory or URI to scan, without trailing slash.

$mask: The preg_match() regular expression of the files to find.

$options: An associative array of additional options, with the following elements:

  • 'nomask': The preg_match() regular expression of the files to ignore. Defaults to '/(\.\.?|CVS)$/'.
  • 'callback': The callback function to call for each match. There is no default callback.
  • 'recurse': When TRUE, the directory scan will recurse the entire tree starting at the provided directory. Defaults to TRUE.
  • 'key': The key to be used for the returned associative array of files. Possible values are 'uri', for the file's URI; 'filename', for the basename of the file; and 'name' for the name of the file without the extension. Defaults to 'uri'.
  • 'min_depth': Minimum depth of directories to return files from. Defaults to 0.

$depth: Current depth of recursion. This parameter is only used internally and should not be passed in.

Return value

An associative array (keyed on the chosen key) of objects with 'uri', 'filename', and 'name' members corresponding to the matching files.

Related topics

20 calls to file_scan_directory()
DrupalWebTestCase::drupalGetTestFiles in modules/simpletest/drupal_web_test_case.php
Get a list files that can be used in tests.
drupal_clear_css_cache in includes/common.inc
Deletes old cached CSS files.
drupal_clear_js_cache in includes/common.inc
Deletes old cached JavaScript files and variables.
drupal_system_listing in includes/common.inc
Returns information about system object files (modules, themes, etc.).
FileScanDirectoryTest::testOptionCallback in modules/simpletest/tests/file.test
Check that the callback function is called correctly.

... See full list


includes/file.inc, line 2132
API for handling file uploads and server file management.


function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {

  // Merge in defaults.
  $options += array(
    'nomask' => '/(\\.\\.?|CVS)$/',
    'callback' => 0,
    'recurse' => TRUE,
    'key' => 'uri',
    'min_depth' => 0,
  $options['key'] = in_array($options['key'], array(
  )) ? $options['key'] : 'uri';
  $files = array();
  if (is_dir($dir) && ($handle = opendir($dir))) {
    while (FALSE !== ($filename = readdir($handle))) {
      if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') {
        $uri = "{$dir}/{$filename}";
        $uri = file_stream_wrapper_uri_normalize($uri);
        if (is_dir($uri) && $options['recurse']) {

          // Give priority to files in this folder by merging them in after any subdirectory files.
          $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files);
        elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) {

          // Always use this match over anything already set in $files with the
          // same $$options['key'].
          $file = new stdClass();
          $file->uri = $uri;
          $file->filename = $filename;
          $file->name = pathinfo($filename, PATHINFO_FILENAME);
          $key = $options['key'];
          $files[$file->{$key}] = $file;
          if ($options['callback']) {
  return $files;


Kasper S’s picture

The uri's that file_scan_directory returns don't pass file_valid_uri().
Which is a bit confusing...

It returns e.g. sites/all/modules/custom/imgimp/pics/test.jpg instead of a string that starts with public://

mikl’s picture

The URIs I get from file_scan_directory() are in the public://old_product_images/1359b.jpg form, so it might be a bug in an earlier vresion you encountered…

gaelicWizard’s picture

Does the $dir parameter need to be an absolute path, a relative path, or either?

Shtefa’s picture

You can use both. But keep in mind that relative paths are checked relative to the Drupal's installation directory.

indytechcook’s picture

The mask can be tricking if you are not great in regular expressions.

Here is an example of retrieving all files from a directory and it's sub-directories that are PDF's.

  $dir = "/some/dir"
  $files = file_scan_directory($dir, '/.*\.pdf$/');

The return format is

  "/full/file/path/" => stdClass
fledev’s picture

After googling and inflectin Drupal in many bad ways, I've found out how to get the files within the default directory:

$files = file_scan_directory('public://fm_video', '/^.*\.(mov|mp4|avi|MOV|MP4|AVI)$/');

where fm_video must have read rights. If you would need only one file type use the '/.*\.jpg$/' like:

$files = file_scan_directory('public://fm_video', '/.*\.jpg$/');

Since regex is a realy difficult thing, I don't undestand the joke of Drupal developers to use mandatorily in a basic function. Hope this will help you further.

scuba_fly’s picture

just use a tool to build and learn regex I used espresso when I still was using windows haven't had the need to use a tool since I've switched to mac but I'm sure there are also tools voor mac and linux.

drupalshrek’s picture

The following example is from Organic Groups (6.x-2.1) module, og_views.views_default.inc which processes the og_views/views directory which contains a list of views files such as:

  • og.og_files.view
  • og.og_ghp_ron.view
  • og.og_members_block.view
// Declare all the .view files in the views subdir that end in .view
function og_views_views_default_views() {
  $files = file_scan_directory(drupal_get_path('module', 'og_views'). '/views', '.view');
  foreach ($files as $absolute => $file) {
    require $absolute;
    if (isset($view)) {
      $views[substr($file->name, 3)] = $view;
  return $views;

drupalfever’s picture

I agree with "frizi" that Regular Expression is a dog!! It is also a very useful and powerful tool. If you are a serious Drupal developer, there is no way around it.

Before I go any further I just wanted to say that I don't work for lynda.com or am associated with this company in any way, shape, and form.

There is a course on lynda.com that really made a difference for me when it came to understanding Regular Expressions. The course is called "Using Regular Expressions" authored by "Kevin Skoglund".

I am not saying that, after watching this 5h 36m tutorial, you will start writing your own regular expressions left and right. I am just saying that it will get you a long way towards becoming less scared of Regular Expressions. That's what happened to me.

Good luck to those of you that are willing to watch this lynda.com tutorial.

Anybody’s picture

Use this example to find all .inc files in the given directory and its subdirectories:

$files = file_scan_directory(drupal_get_path('module', 'example'). '/includes', '/\.inc/');
swirt’s picture

Notes on the nomask option. Nomask does not run against the entire file path and filename at once, it only looks at each segment as it goes. So in order to get it to block a directory you need a nomask regex designed to evaluate just a single directory.
Example: to block a directory "deleted" the regex would look like this:

$options['nomask'] = "#^(deleted)$#";