Dir Tree Iterator

lua-users home
wiki

The following implementation provides a Lua iterator function that recursively iterates over the files and subdirectories in a given directory.

It depends on the LuaFileSystem library ("lfs") [1].

-- code by AlexanderMarinov
-- Compatible with Lua 5.1 (not 5.0).
require "lfs"

function dirtree(dir)
  assert(dir and dir ~= "", "directory parameter is missing or empty")
  if string.sub(dir, -1) == "/" then
    dir=string.sub(dir, 1, -2)
  end

  local diriters = {lfs.dir(dir)}
  local dirs = {dir}

  return function()
    repeat 
      local entry = diriters[#diriters]()
      if entry then 
        if entry ~= "." and entry ~= ".." then 
          local filename = table.concat(dirs, "/").."/"..entry
          local attr = lfs.attributes(filename)
          if attr.mode == "directory" then 
            table.insert(dirs, entry)
            table.insert(diriters, lfs.dir(filename))
          end
          return filename, attr
        end
      else
        table.remove(dirs)
        table.remove(diriters)
      end
    until #diriters==0
  end
end

A more concise version using coroutines would be

-- Code by David Kastrup
require "lfs"

function dirtree(dir)
  assert(dir and dir ~= "", "directory parameter is missing or empty")
  if string.sub(dir, -1) == "/" then
    dir=string.sub(dir, 1, -2)
  end

  local function yieldtree(dir)
    for entry in lfs.dir(dir) do
      if entry ~= "." and entry ~= ".." then
        entry=dir.."/"..entry
	local attr=lfs.attributes(entry)
	coroutine.yield(entry,attr)
	if attr.mode == "directory" then
	  yieldtree(entry)
	end
      end
    end
  end

  return coroutine.wrap(function() yieldtree(dir) end)
end

Example usage when the current directory (.) is the Lua source directory:

> -- example
> for filename, attr in dirtree(".") do
>>      print(attr.mode, filename)
>> end
file    ./COPYRIGHT
directory       ./doc
file    ./doc/contents.html
file    ./doc/logo.gif
file    ./doc/lua.1
file    ./doc/lua.css
file    ./doc/lua.html
file    ./doc/luac.1
file    ./doc/luac.html
file    ./doc/manual.html
file    ./doc/readme.html
directory       ./etc
file    ./etc/all.c
file    ./etc/lua.hpp
file    ./etc/lua.ico
file    ./etc/lua.pc
file    ./etc/luavs.bat
file    ./etc/Makefile
file    ./etc/min.c
file    ./etc/noparser.c
file    ./etc/README
file    ./etc/strict.lua
file    ./HISTORY
file    ./INSTALL
file    ./Makefile
file    ./README
directory       ./src
file    ./src/lapi.c
file    ./src/lapi.h
file    ./src/lauxlib.c
file    ./src/lauxlib.h
file    ./src/lbaselib.c
file    ./src/lcode.c
file    ./src/lcode.h
file    ./src/ldblib.c
file    ./src/ldebug.c
file    ./src/ldebug.h
file    ./src/ldo.c
file    ./src/ldo.h
file    ./src/ldump.c
file    ./src/lfunc.c
file    ./src/lfunc.h
file    ./src/lgc.c
file    ./src/lgc.h
file    ./src/linit.c
file    ./src/liolib.c
file    ./src/llex.c
file    ./src/llex.h
file    ./src/llimits.h
file    ./src/lmathlib.c
file    ./src/lmem.c
file    ./src/lmem.h
file    ./src/loadlib.c
file    ./src/lobject.c
file    ./src/lobject.h
file    ./src/lopcodes.c
file    ./src/lopcodes.h
file    ./src/loslib.c
file    ./src/lparser.c
file    ./src/lparser.h
file    ./src/lstate.c
file    ./src/lstate.h
file    ./src/lstring.c
file    ./src/lstring.h
file    ./src/lstrlib.c
file    ./src/ltable.c
file    ./src/ltable.h
file    ./src/ltablib.c
file    ./src/ltm.c
file    ./src/ltm.h
file    ./src/lua.c
file    ./src/lua.h
file    ./src/luac.c
file    ./src/luaconf.h
file    ./src/lualib.h
file    ./src/lundump.c
file    ./src/lundump.h
file    ./src/lvm.c
file    ./src/lvm.h
file    ./src/lzio.c
file    ./src/lzio.h
file    ./src/Makefile
file    ./src/print.c
directory       ./test
file    ./test/bisect.lua
file    ./test/cf.lua
file    ./test/echo.lua
file    ./test/env.lua
file    ./test/factorial.lua
file    ./test/fib.lua
file    ./test/fibfor.lua
file    ./test/globals.lua
file    ./test/hello.lua
file    ./test/life.lua
file    ./test/luac.lua
file    ./test/printf.lua
file    ./test/README
file    ./test/readonly.lua
file    ./test/sieve.lua
file    ./test/sort.lua
file    ./test/table.lua
file    ./test/trace-calls.lua
file    ./test/trace-globals.lua
file    ./test/xd.lua

Another method using "lfs".

require "lfs"

-- code by GianlucaVespignani - 2012-03-04; 2013-01-26
-- Search files in a path, alternative in sub directory
-- @param dir_path string (";" for multiple paths supported)
-- @param filter string - eg.: ".txt" or ".mp3;.wav;.flac"
-- @param s bool - search in subdirectories
-- @param pformat format of data - 'system' for system-dependent number; nil or string with formatting directives
-- @return  files, dirs - files and dir are tables {name, modification, path, size}
function file_search(dir_path, filter, s, pformat)
	-- === Preliminary functions ===
	-- comparison function like the IN() function like SQLlite, item in a array
	-- useful for compair table for escaping already processed item
	-- Gianluca Vespignani 2012-03-03
	local c_in = function(value, tab)
		for k,v in pairs(tab) do
			if v==value then
				return true
			end
		end
		return false
	end

	local string = string	-- http://lua-users.org/wiki/SplitJoin
	function string:split(sep)
		local sep, fields = sep or ":", {}
		local pattern = string.format("([^%s]+)", sep)
		self:gsub(pattern, function(c) fields[#fields+1] = c end)
		return fields
	end

	local ExtensionOfFile = function(filename)
		local rev = string.reverse(filename)
		local len = rev:find("%.")
		local rev_ext = rev:sub(1,len)
		return string.reverse(rev_ext)
	end

	-- === Init ===
	dir_path = dir_path or cwd
	filter = string.lower(filter) or "*"
	extensions = filter:split(";")
	s = s or false -- as /s : subdirectories

	if pformat == 'system' then	-- if 4th arg is explicity 'system', then return the system-dependent number representing date/time
		os_date = function(os_time)
			return os_time
		end
	else
		-- if 4th arg is nil use default, else it could be a string that respects the Time formatting directives
		pformat = pformat or "%Y/%m/%d" -- eg.: "%Y/%m/%d %H:%M:%S"
		os_date = function(os_time)
			return os.date(pformat, os_time)
		end
	end

	-- == MAIN ==
	local files = {}
	local dirs = {}
	local paths = dir_path:split(";")
	for i,path in ipairs(paths) do
		for f in lfs.dir(path) do
			if f ~= "." and f ~= ".." then
				local attr = lfs.attributes ( path.."/"..f )
				if attr.mode == "file" then
					if filter=="*"
					or c_in( string.lower( ExtensionOfFile(f) ), extensions)
					then
						table.insert(files,{
							name = f,
							modification = os_date(attr.modification) ,
							path = path.."/",
							size = attr.size
						})
					end
				else
					if filter=="*" then			-- if attr.mode == "directory" and file ~= "." and file ~= ".." then end
						table.insert(dirs,{
							name = f ,
							modification = os_date(attr.modification) ,
							path = path.."/",
							size = attr.size
						})
					end
					if s and attr.mode == "directory" then
						local subf={}
						local subd={}
						subf, subd = file_search(path.."/"..f, filter, s, pformat)
						for i,v in ipairs(subf) do
							table.insert(files,{
								name = v.name ,
								modification = v.modification ,
								path = v.path,
								size = v.size
							})
						end
						for i,v in ipairs(subd) do
							table.insert(dirs,{
								name = v.name ,
								modification = v.modification ,
								path = v.path,
								size = v.size
							})
						end
					end
				end
			end
		end
	end
	return files,dirs

	--[=[	ABOUT ATTRIBUTES
> for k,v in pairs(a) do print(k..' \t'..v..'') end
dev     2
change  1175551262	-- date of file Creation
access  1235831652
rdev    2
nlink   1
uid     0
gid     0
ino     0
mode    file
modification    1181692021 -- Date of Last Modification
size    805 in byte
	]=]
end

RecentChanges · preferences
edit · history
Last edited January 26, 2013 6:17 pm GMT (diff)