Walk a directory/Recursively
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
Walk a given directory tree and print files matching a given pattern.
Note: Please be careful when running any code examples found here.
Ada
<lang ada>with Ada.Directories; use Ada.Directories; with Ada.Text_IO;
procedure Test_Directory_Walk is
procedure Walk (Name : String; Pattern : String) is procedure Print (Item : Directory_Entry_Type) is begin Ada.Text_IO.Put_Line (Full_Name (Item)); end Print; procedure Walk (Item : Directory_Entry_Type) is begin if Simple_Name (Item) /= "." and then Simple_Name (Item) /= ".." then Walk (Full_Name (Item), Pattern); end if; exception when Name_Error => null; end Walk; begin Search (Name, Pattern, (others => True), Print'Access); Search (Name, "", (Directory => True, others => False), Walk'Access); end Walk;
begin
Walk (".", "*.adb");
end Test_Directory_Walk;</lang> The solution first enumerates files in a directory, that includes the subdirectories, if their names match the pattern. Then it steps down into each of the subdirectories. The pseudo directories . and .. are excluded. The behavior upon symbolic links depends on the OS and the implementation of the Ada.Directories package.
ALGOL 68
<lang algol68>INT match=0, no match=1, out of memory error=2, other error=3;
STRING slash = "/", pwd=".", parent="..";
PROC walk tree = (STRING path, PROC (STRING)VOID call back)VOID: (
[]STRING files = get directory(path); FOR file index TO UPB files DO STRING file = files[file index]; STRING path file = path+slash+file; IF file is directory(path file) THEN IF file NE pwd AND file NE parent THEN walk tree(path file, call back) FI ELSE call back(path file) FI OD
);
STRING re sort a68 = "[Ss]ort[^/]*[.]a68$";
PROC match sort a68 and print = (STRING path file)VOID:
IF grep in string(re sort a68, path file, NIL, NIL) = match THEN print((path file, new line)) FI;
walk tree(".", match sort a68 and print)</lang> Sample Output:
./Shell_sort_c.a68 ./Quick_sort.a68 ./Shell_sort.a68 ./Cocktail_Sort.a68 ./Selection_Sort.a68 ./Merge_sort.a68 ./tmp/test_sort.a68 ./Bobosort.a68 ./Sorting_an_Array_of_Integers.a68 ./Insertion_Sort.a68 ./Permutation_Sort.a68
AutoHotkey
Display all TMP files in Temp directory and its subdirectories. <lang autohotkey>Loop, %A_Temp%\*.tmp,,1
out .= A_LoopFileName "`n"
MsgBox,% out</lang>
C
This is a (recursive) extension of the code at Walk Directory. <lang c>#include <sys/types.h>
- include <sys/stat.h>
- include <unistd.h>
- include <dirent.h>
- include <regex.h>
- include <stdio.h>
- define MAXPD 1024
void walker(const char *dir, const char *pattern) {
struct dirent *entry; regex_t reg; DIR *d; struct stat fs; static int indent = 0; int i; char pd[MAXPD];
if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB)) return; if (!(d = opendir(dir))) return; while (entry = readdir(d)) { if ( ( strcmp(".", entry->d_name) == 0 ) || ( strcmp("..", entry->d_name) == 0 ) ) continue; if ( stat(entry->d_name, &fs) < 0 ) return; if ( S_ISDIR(fs.st_mode) ) {
for(i=0; i < indent; i++) printf(" "); puts(entry->d_name); indent += 2; if ( getcwd(pd, MAXPD) == NULL ) return; if ( chdir(entry->d_name) < 0 ) return; walker(".", pattern); if ( chdir(pd) < 0 ) return; indent -= 2;
} else {
if (!regexec(®, entry->d_name, 0, NULL, 0)) { for(i=0; i < indent; i++) printf(" "); puts(entry->d_name); }
} } closedir(d);
}
int main() {
walker(".", ".\\.c$"); return 0;
}</lang>
Common Lisp
This example uses the CL-FAD
library to achieve compatibility where the ANSI CL standard leaves ambiguities about pathnames.
<lang lisp>(defun mapc-directory-tree (fn directory)
(dolist (entry (cl-fad:list-directory directory)) (when (cl-fad:directory-pathname-p entry) (mapc-directory-tree fn entry)) (funcall fn entry)))</lang>
<lang lisp>CL-USER> (mapc-directory-tree (lambda (x)
(when (equal (pathname-type x) "lisp") (write-line (namestring x)))) "lang/")
/home/sthalik/lang/lisp/.#bitmap.lisp /home/sthalik/lang/lisp/avg.lisp /home/sthalik/lang/lisp/bitmap.lisp /home/sthalik/lang/lisp/box-muller.lisp /home/sthalik/lang/lisp/displaced-subseq.lisp [...]</lang>
C++
<lang cpp>#include "boost/filesystem.hpp"
- include "boost/regex.hpp"
- include <iostream>
using namespace boost::filesystem;
int main() {
path current_dir("."); // boost::regex pattern("a.*"); // list all files starting with a for (recursive_directory_iterator iter(current_dir), end; iter != end; ++iter) { std::string name = iter->path().leaf(); if (regex_match(name, pattern)) std::cout << iter->path() << "\n"; }
}</lang>
D
module std.file provides different walk directory functions (listdir).
This one recursively walks the directory, which can either match by regular expression or unix shell style pattern.
<lang d>import std.stdio;
import std.file;
import std.regexp;
void main(string[] args) {
auto path = args.length > 1 ? args[1] : "." ; // default current auto pattern = args.length > 2 ? args[2] : "*.*"; // default all file bool useRegExp = (args.length > 3 && args[3] == "-re") ; // pattern matching method
if (args.length > 3 && args[3] == "-re") // use Regular Expression foreach (d; listdir(path, RegExp(pattern))) writefln(d); else // use unix shell style pattern matching foreach (d; listdir(path, pattern)) writefln(d); }</lang>
This one does not itself walk into a sub directory, but can be recursive by a callback delegate function. <lang d>import std.stdio; import std.file; import std.regexp; import std.path ;
void main(string[] args) {
auto path = args.length > 1 ? args[1] : "." ; // default current auto pattern = args.length > 2 ? args[2] : "*.*"; // default all file bool useRegExp = (args.length > 3 && args[3] == "-re") ; // pattern matching method bool recursive = (args.length <= 4 || args[4] != "-nr") ; // recursive?
bool matchNPrint(DirEntry* de) { bool bPrint = false ; if(!de.isdir) { if(useRegExp){ if(search(de.name, pattern)) // this _search_ from regexp module writefln(de.name) ; }else{ if(fnmatch(de.name, pattern)) // this _fnmatch_ from path module writefln(de.name) ; } } else if(recursive) listdir(de.name, &matchNPrint) ; // recursive sub dir return true ; // continue } listdir(path, &matchNPrint) ; }</lang>
DOS Batch File
<lang dos>dir /a-d %1</lang>
If you wanted to apply some command to each item in a directory tree, then use /R
on a FOR
. For example, apply the ECHO command to every DLL file in C:\Windows\System32:
<lang dos>FOR /R C:\Windows\System32 %F IN (*.DLL) DO ECHO "%F"</lang>
E
<lang e>def walkTree(directory, pattern) {
for name => file in directory { if (name =~ rx`.*$pattern.*`) { println(file.getPath()) } if (file.isDirectory()) { walkTree(file, pattern) } }
}</lang>
Example:
<lang e>? walkTree(<file:/usr/share/man>, "rmdir") /usr/share/man/man1/rmdir.1 /usr/share/man/man2/rmdir.2</lang>
F#
This code is tail-recursive and lazy. <lang fsharp> open System.IO
let rec getAllFiles dir pattern =
seq { yield! Directory.EnumerateFiles(dir, pattern) for d in Directory.EnumerateDirectories(dir) do yield! getAllFiles d pattern }
getAllFiles "c:\\temp" "*.xml" |> Seq.iter (printfn "%s") </lang>
Factor
<lang factor>USE: io.directories.search
"." t [
dup ".factor" tail? [ print ] [ drop ] if
] each-file</lang>
Forth
Todo: track the full path and print it on matching files.
<lang forth>defer ls-filter
- dots? ( name len -- ? )
dup 1 = if drop c@ [char] . = else 2 = if dup c@ [char] . = swap 1+ c@ [char] . = and else drop false then then ;
- ls-r ( dir len -- )
open-dir if drop exit then ( dirid) begin dup pad 256 rot read-dir throw while pad over dots? 0= if \ ignore current and parent dirs pad over recurse pad over ls-filter if cr pad swap type else drop then else drop then repeat drop close-dir throw ;
- c-file? ( str len -- ? )
dup 3 < if 2drop false exit then + 1- dup c@ 32 or dup [char] c <> swap [char] h <> and if drop false exit then 1- dup c@ [char] . <> if drop false exit then drop true ;
' c-file? is ls-filter
s" ." ls-r</lang>
Groovy
Print all text files in the current directory tree <lang groovy>new File('.').eachFileRecurse {
if (it.name =~ /.*\.txt/) println it;
}</lang>
Haskell
<lang haskell>import System.Environment import System.Directory import System.FilePath.Find
search pat dir =
find always (fileName ~~? pat) dir
main = do [pat] <- getArgs
dir <- getCurrentDirectory files <- search pat dir mapM_ putStrLn files</lang>
IDL
<lang idl>result = file_search( directory, '*.txt', count=cc )</lang>
This will descend down the directory/ies in the variable "directory" (which can be an array) returning an array of strings with the names of the files matching "*.txt" and placing the total number of matches into the variable "cc"
J
<lang j>require 'dir' >{."1 dirtree '*.html'</lang>
The verb dirtree returns a file listing of a directory tree as a boxed matrix with file names in the first column. The primitives >{."1 will return the unboxed contents of the first column.
Java
Done using no pattern. But with end string comparison which gave better results.
<lang java>import java.io.File; public class MainEntry {
public static void main(String[] args) { walkin(new File("/home/user")); //Replace this with a suitable directory } /** * Recursive function to descend into the directory tree and find all the files * that end with ".mp3" * @param dir A file object defining the top directory **/ public static void walkin(File dir) { String pattern = ".mp3"; File listFile[] = dir.listFiles(); if(listFile != null) { for(int i=0; i<listFile.length; i++) { if(listFile[i].isDirectory()) { walkin(listFile[i]); } else { if(listFile[i].getName().endsWith(pattern)) { System.out.println(listFile[i].getPath()); } } } } }
}</lang>
JavaScript
<lang javascript>var fso = new ActiveXObject("Scripting.FileSystemObject");
function walkDirectoryTree(folder, folder_name, re_pattern) {
WScript.Echo("Files in " + folder_name + " matching '" + re_pattern + "':"); walkDirectoryFilter(folder.files, re_pattern);
var subfolders = folder.SubFolders; WScript.Echo("Folders in " + folder_name + " matching '" + re_pattern + "':"); walkDirectoryFilter(subfolders, re_pattern);
WScript.Echo(); var en = new Enumerator(subfolders); while (! en.atEnd()) { var subfolder = en.item(); walkDirectoryTree(subfolder, folder_name + "/" + subfolder.name, re_pattern); en.moveNext(); }
}
function walkDirectoryFilter(items, re_pattern) {
var e = new Enumerator(items); while (! e.atEnd()) { var item = e.item(); if (item.name.match(re_pattern)) WScript.Echo(item.name); e.moveNext(); }
}
walkDirectoryTree(dir, dir.name, '\\.txt$');</lang>
Mathematica
The built-in function FileNames does exactly this: <lang Mathematica>FileNames[] lists all files in the current working directory. FileNames[form] lists all files in the current working directory whose names match the string pattern form. FileNames[{form1,form2,...}] lists all files whose names match any of the form_i. FileNames[forms,{dir1,dir2,...}] lists files with names matching forms in any of the directories dir_i. FileNames[forms,dirs,n] includes files that are in subdirectories up to n levels down.</lang> Examples (find all files in current directory, find all png files in root directory, find all files on the hard drive): <lang Mathematica>FileNames["*"] FileNames["*.png", $RootDirectory] FileNames["*", {"*"}, Infinity]</lang> the result can be printed with Print /@ FileNames[....]
MAXScript
<lang maxscript>fn walkDir dir pattern = (
dirArr = GetDirectories (dir + "\\*")
for d in dirArr do ( join dirArr (getDirectories (d + "\\*")) )
append dirArr (dir + "\\") -- Need to include the original top level directory
for f in dirArr do ( print (getFiles (f + pattern)) )
)
walkDir "C:" "*.txt"</lang>
Objective-C
<lang objc>NSString *dir = NSHomeDirectory(); NSDirectoryEnumerator *de = [[NSFileManager defaultManager] enumeratorAtPath:dir];
NSString *file; while ((file = [de nextObject]))
if ([[file pathExtension] isEqualToString:@"mp3"]) NSLog(@"%@", file);</lang>
OCaml
<lang ocaml>#!/usr/bin/env ocaml
- load "unix.cma"
- load "str.cma"
open Unix
let walk_directory_tree dir pattern =
let select str = Str.string_match (Str.regexp pattern) str 0 in let rec walk acc = function | [] -> (acc) | dir::tail -> let contents = Array.to_list (Sys.readdir dir) in let contents = List.rev_map (Filename.concat dir) contents in let dirs, files = List.fold_left (fun (dirs,files) f -> match (stat f).st_kind with | S_REG -> (dirs, f::files) (* Regular file *) | S_DIR -> (f::dirs, files) (* Directory *) | _ -> (dirs, files) ) ([],[]) contents in let matched = List.filter (select) files in walk (matched @ acc) (dirs @ tail) in walk [] [dir]
let () =
let results = walk_directory_tree "/usr/local/lib/ocaml" ".*\\.cma" in List.iter print_endline results;
- </lang>
Oz
<lang oz>declare
[Path] = {Module.link ['x-oz://system/os/Path.ozf']} [Regex] = {Module.link ['x-oz://contrib/regex']}
proc {WalkDirTree Root Pattern Proc} proc {Walk R} Entries = {Path.readdir R} Files = {Filter Entries Path.isFile} MatchingFiles = {Filter Files fun {$ File} {Regex.search Pattern File} \= false end} Subdirs = {Filter Entries Path.isDir} in {ForAll MatchingFiles Proc} {ForAll Subdirs Walk} end in {Walk Root} end
in
{WalkDirTree "." ".*\\.oz$" System.showInfo}</lang>
Perl
<lang perl>use File::Find qw(find); my $dir = '.'; my $pattern = 'foo'; find sub {print $File::Find::name if /$pattern/}, $dir;</lang>
PHP
<lang PHP>function findFiles($dir = '.', $pattern = '/./'){
$prefix = $dir . '/'; $dir = dir($dir); while (false !== ($file = $dir->read())){ if ($file === '.' || $file === '..') continue; $file = $prefix . $file; if (is_dir($file)) findFiles($file, $pattern); if (preg_match($pattern, $file)){ echo $file . "\n"; } }
} findFiles('./foo', '/\.bar$/');</lang>
This implementation uses Perl compatible regular expressions to match the whole path of the file
PicoLisp
<lang PicoLisp>(let Dir "."
(recur (Dir) (for F (dir Dir) (let Path (pack Dir "/" F) (cond ((=T (car (info Path))) # Is a subdirectory? (recurse Path) ) # Yes: Recurse ((match '`(chop "s@.l") (chop F)) # Matches 's*.l'? (println Path) ) ) ) ) ) ) # Yes: Print it</lang>
Output:
"./src64/sym.l" "./src64/subr.l" ...
Pop11
Built-in procedure sys_file_match searches directories or directory trees using shell-like patterns (three dots indicate search for subdirectory tree). <lang pop11>lvars repp, fil;
- create path repeater
sys_file_match('.../*.p', , false, 0) -> repp;
- iterate over paths
while (repp() ->> fil) /= termin do
;;; print the path printf(fil, '%s\n');
endwhile;</lang>
Python
This uses the standard os.walk() "generator".
<lang python>import fnmatch import os
rootPath = '/' pattern = '*.mp3' # Can include any UNIX shell-style wildcards
for root, dirs, files in os.walk(rootPath):
for filename in fnmatch.filter(files, pattern): print os.path.join(root, filename)</lang>
(deprecated; removed in 3.0)
A more strictly comparable port of this 2.3+ code to earlier versions of Python would be:
<lang python>from fnmatch import fnmatch import os, os.path
def print_fnmatches(pattern, dir, files):
for filename in files: if fnmatch(name, pattern): print os.path.join(dir, filename)
os.path.walk('/', print_fnmatches, '*.mp3')</lang>
The old os.path.walk function was a challenge for many to use because of the need to pass a function into the walk, and any arguments to that function through to it ... as shown. It's sometimes useful to pass mutable objects (lists, dictionaries, or instances of user-defined classes) to the inner function ... for example, to collect all the matching files for later processing.
Of course the function being passed down through os.path.walk() can also be an instance of an object which maintains it's own data collections. Any matching criteria can be set as attributes of that object in advance and methods of that object can be called upon for later processing as well. That would the an object oriented approach which would obviate the need for the "arguments" to be passed through os.path.walk() at all.
(Note: This uses a non-standard replacement to the os.path module)
<lang python>from path import path
rootPath = '/' pattern = '*.mp3'
d = path(rootPath) for f in d.walkfiles(pattern):
print f</lang>
PowerShell
In PowerShell the Get-ChildItem
cmdlet allows for recursive filtering on file names with simple wildcards:
<lang powershell>Get-ChildItem -Recurse -Include *.mp3</lang>
For more complex filtering criteria the result of Get-ChildItem
can be piped into the Where-Object
cmdlet:
<lang powershell>Get-ChildItem -Recurse |
Where-Object { $_.Name -match 'foo[0-9]' -and $_.Length -gt 5MB }</lang>
To perform an action on every matching file the results can be piped into the ForEach-Object
cmdlet:
<lang powershell>Get-ChildItem -Recurse |
Where-Object { $_.Name -match 'foo[0-9]' } | ForEach-Object { ... }</lang>
Note: To include only files instead of directories too each of the above needs an additionalWhere-Object
filter:
<lang powershell>| Where-Object { !$_.PSIsContainer }</lang>
R
<lang R>dir("/bar/foo", "mp3",recursive=T)</lang>
Ruby
Pattern matching using regular expressions <lang ruby>#define a recursive function that will traverse the directory tree def printAndDescend(pattern)
#we keep track of the directories, to be used in the second, recursive part of this function directories=[] Dir['*'].sort.each do |name| if File.file?(name) and name[pattern] puts(File.expand_path(name)) elsif File.directory?(name) directories << name end end directories.each do |name| #don't descend into . or .. on linux Dir.chdir(name){printAndDescend(pattern)} if !Dir.pwd[File.expand_path(name)] end
end
- print all ruby files
printAndDescend(/.+\.rb$/)</lang>
Or use the Find core Module
<lang ruby>require 'find'
def find_and_print(path, pattern)
Find.find(path) do |entry| if File.file?(entry) and entry[pattern] puts entry end end
end
- print all the ruby files
find_and_print(".", /.+\.rb$/)</lang>
Or, to find and print all files under '/foo/bar' the easy way: <lang ruby> Dir.glob( File.join('/foo/bar', '**', '*') ) { |file| puts file }</lang>
Scala
This is not implemented in the Scala library. Here is a possible solution, building on class java.io.File and on scala language and library iteration facilities
<lang scala>package io.utils
import java.io.File
/** A wrapper around file, allowing iteration either on direct children
or on directory tree */
class RichFile(file: File) {
def children = new Iterable[File] { def elements = if (file.isDirectory) file.listFiles.elements else Iterator.empty; }
def andTree : Iterable[File] = ( Seq.single(file) ++ children.flatMap(child => new RichFile(child).andTree))
}
/** implicitely enrich java.io.File with methods of RichFile */ object RichFile {
implicit def toRichFile(file: File) = new RichFile(file)
}</lang>
Class RichFile gets a java.io.File in constructor. Its two methods return Iterables on items of type File. children allow iterations on the direct children (empty if file is not a directory). andTree contains a file and all files below, as a concatenation (++) of a sequence which contains only a file (Seq.single) and actual descendants. The method flatMap in Iterable takes a function argument which associates each item (child) to another Iterable (andTree called recursively on that child) and returns the concatenation of those iterables.
The purpose of the object RichFile is to publish the implicit method toRichFile. When this method is available in scope (after import RichFile.toRichFile or import RichFile._), it is called behind the scene when a method of class RichFile is called on an instance of type File : with f of type File, code f.children (resp. f.andTree) becomes toRichFile(f).children (resp. toRichFile(f).andTree). It is as if class File had been added the methods of class RichFile.
Using it : <lang scala>package test.io.utils
import io.utils.RichFile._ // this makes implicit toRichFile active import java.io.File
object Test extends Application {
val root = new File("/home/user") for(f <- root.andTree) Console.println(f)
// filtering comes for free for(f <- root.andTree; if f.getName.endsWith(".mp3")) Console.println(f)
}</lang>
Smalltalk
<lang smalltalk>Directory extend [
wholeContent: aPattern do: twoBlock [ self wholeContent: aPattern withLevel: 0 do: twoBlock. ] wholeContent: aPattern withLevel: l do: twoBlock [ |cont| cont := (self contents) asSortedCollection. cont remove: '.'; remove: '..'. cont do: [ :n | |fn ps| ps := (Directory pathSeparator) asString. fn := (self name), ps, n. ((File name: fn) isDirectory) ifTrue: [ twoBlock value: (n, ps) value: l.
(Directory name: fn) wholeContent: aPattern withLevel: (l+1) do: twoBlock.
] ifFalse: [ ( n =~ aPattern ) ifMatched: [ :m | twoBlock value: n value: l ] ] ] ]
].</lang>
<lang smalltalk>|d| d := Directory name: '.'. d wholeContent: '\.st$' do: [ :f :l |
0 to: l do: [ :i | (Character tab) display ]. f displayNl
].</lang>
Tcl
<lang tcl>proc walkin {fromDir pattern} {
foreach fname [glob -nocomplain -directory $fromDir *] { if {[file isdirectory $fname]} { walkin $fname $pattern } elseif {[string match $pattern [file tail $fname]]} { puts [file normalize $fname] } }
}
- replace directory with something appropriate
walkin /home/user *.mp3</lang>
Visual Basic .NET
This uses the OS pattern matching
<lang vbnet>Sub walkTree(ByVal directory As IO.DirectoryInfo, ByVal pattern As String)
For Each file In directory.GetFiles(pattern) Console.WriteLine(file.FullName) Next For Each subDir In directory.GetDirectories walkTree(subDir, pattern) Next
End Sub</lang>
UNIX Shell
<lang bash>#! /bin/bash
indent_print() {
for((i=0; i < $1; i++)); do
echo -ne "\t"
done echo "$2"
}
walk_tree() {
local oldifs bn lev pr pmat if $# -lt 3 ; then
if $# -lt 2 ; then pmat=".*" else pmat="$2" fi walk_tree "$1" "$pmat" 0 return
fi lev=$3 [ -d "$1" ] || return oldifs=$IFS IFS="
"
for el in $1/*; do
bn=$(basename "$el") if -d "$el" ; then indent_print $lev "$bn/" pr=$( walk_tree "$el" "$2" $(( lev + 1)) ) echo "$pr" else if "$bn" =~ $2 ; then indent_print $lev "$bn" fi fi
done IFS=$oldifs
}
walk_tree "$1" "\.sh$"</lang>
A simplified version that gives the same output:
<lang bash>#! /usr/bin/env bash
walk_tree() { ls "$1" | while IFS= read i; do if [ -d "$1/$i" ]; then echo "$i/" walk_tree "$1/$i" "$2" | sed -r 's/^/\t/' else echo "$i" | grep -E "$2" fi done }
walk_tree "$1" "\.sh$"</lang>
UnixPipes
Find accepts file globbing params too as -name, here I use regexp from grep. <lang bash>find . | grep '.*\.txt$'</lang>
- Programming Tasks
- File System Operations
- Recursion
- Ada
- ALGOL 68
- AutoHotkey
- C
- Common Lisp
- CL-FAD
- C++
- Boost
- D
- DOS Batch File
- E
- F Sharp
- Factor
- Forth
- Groovy
- Haskell
- IDL
- J
- Java
- JavaScript
- Mathematica
- MAXScript
- Objective-C
- OCaml
- Oz
- Perl
- PHP
- PicoLisp
- Pop11
- Python
- Path
- PowerShell
- R
- Ruby
- Scala
- Smalltalk
- Tcl
- Visual Basic .NET
- UNIX Shell
- UnixPipes
- TI-89 BASIC/Omit
- M4/Omit
- Befunge/Omit