Walk Directory Tree
From Rosetta Code
Programming Task
This is a programming task. It lays out a problem which Rosetta Code users are encouraged to solve, using languages they know.
Walk a given directory tree and print files matching a given pattern.
Note: Please be careful when running any code examples found here.
Contents |
[edit] 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;
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.
[edit] 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.
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);
}
This one does not itself walk into a sub directory, but can be recursive by a callback delegate function.
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) ;
}
[edit] E
def walkTree(directory, pattern) {
for name => file in directory {
if (name =~ rx`.*$pattern.*`) {
println(file.getPath())
}
if (file.isDirectory()) {
walkTree(file, pattern)
}
}
}
Example:
? walkTree(<file:/usr/share/man>, "rmdir") /usr/share/man/man1/rmdir.1 /usr/share/man/man2/rmdir.2
[edit] Forth
Works with: gforth version 0.6.2
Todo: track the full path and print it on matching files.
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
[edit] Groovy
Print all text files in the current directory tree
new File('.').eachFileRecurse {
if (it.name =~ /.*\.txt/) println it;
}
[edit] IDL
result = file_search( directory, '*.txt', count=cc )
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"
[edit] Java
Works with: Java version 1.4+
Done using no pattern. But with end string comparison which gave better results.
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()); } } } } } }
[edit] 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"
[edit] 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 dir = 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 (* recursively walk into sub-directories: *) let results = List.fold_left (fun acc dir -> let sub_result = walk dir in List.rev_append sub_result acc ) matched dirs in (results) in walk dir ;; let () = let results = walk_directory_tree "/usr/local/lib/ocaml" ".*\\.cma" in List.iter print_endline results; ;;
[edit] Perl
Works with: Perl version 5.x
use File::Find qw(find);
my $dir = '.';
my $pattern = 'foo';
find sub {print $File::Find::name if /$pattern/}, $dir;
[edit] Pop11
Built-in procedure sys_file_match searches directories or directory trees using shell-like patterns (three dots indicate search for subdirectory tree).
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;
[edit] Python
Works with: Python version 2.5
This uses the standard os.walk() "generator".
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 files: if fnmatch.fnmatch(filename, pattern): print os.path.join(root, filename)
Works with: Python version <2.2
A more strictly comparable port of this 2.5 code to earlier versions of Python would be:
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')
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.
Works with: Python version 2.5
Library: Path
(Note: This uses a non-standard replacement to the os.path module)
from path import path rootPath = '/' pattern = '*.mp3' d = path(rootPath) for f in d.walkfiles(pattern): print f
[edit] Ruby
Pattern matching using regular expressions
#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$/)
Or use the Find core Module
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$/)
Or, to find and print all files under '/foo/bar' the easy way:
Dir.glob( File.join('/foo/bar', '**', '*') ) { |file| puts file }
[edit] 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
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)
}
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 :
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)
}
[edit] Tcl
Works with: Tcl version 8.4
proc walkin { fromDir } {
foreach fname [glob -nocomplain -directory $fromDir *] {
if { [file isdirectory $fname] } {
walkin $fname
} else {
if { [string match *.mp3 $fname] } {
puts [file normalize $fname]
}
}
}
}
# replace directory with something appropriate
walkin /home/user
[edit] Visual Basic .NET
Platform: .NET
Works with: Visual Basic .NET version 9.0+
This uses the OS pattern matching
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
[edit] UNIX Shell
Works with: Bourne Again SHell
tree -fi /usr/share/man/ | grep printf /usr/share/man/fr/man1/printf.1.bz2 /usr/share/man/fr/man3/asprintf.3.bz2 /usr/share/man/fr/man3/dprintf.3.bz2 /usr/share/man/fr/man3/fprintf.3.bz2 /usr/share/man/fr/man3/fwprintf.3.bz2 /usr/share/man/fr/man3/printf.3.bz2 /usr/share/man/fr/man3/snprintf.3.bz2 /usr/share/man/fr/man3/sprintf.3.bz2 /usr/share/man/fr/man3/swprintf.3.bz2 /usr/share/man/fr/man3/vasprintf.3.bz2 /usr/share/man/fr/man3/vdprintf.3.bz2 /usr/share/man/fr/man3/vfprintf.3.bz2 /usr/share/man/fr/man3/vfwprintf.3.bz2 /usr/share/man/fr/man3/vprintf.3.bz2 /usr/share/man/fr/man3/vsnprintf.3.bz2 /usr/share/man/fr/man3/vsprintf.3.bz2 /usr/share/man/fr/man3/vswprintf.3.bz2 /usr/share/man/fr/man3/vwprintf.3.bz2 /usr/share/man/fr/man3/wprintf.3.bz2 /usr/share/man/man1/printf.1.bz2 /usr/share/man/man3/ber_printf.3.bz2 /usr/share/man/man3/curl_mprintf.3.bz2
works with all - standard commands are fair game.
find /usr/share/man -name '*printf*'
[edit] UnixPipes
Find accepts file globbing params too as -name, here I use regexp from grep.
find . | grep '.*\.txt$'
Categories: Less Than 20 Examples | Programming Tasks | File System Operations | Recursion | Ada | D | E | Forth | Groovy | IDL | Java | MAXScript | OCaml | Perl | Pop11 | Python | Path | Ruby | Scala | Tcl | Visual Basic .NET | UNIX Shell | UnixPipes

