Dir Tree Iterator

lua-users home
wiki

Showing revision 8
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
-- Search files in a path, alternative in sub directory
-- @param dir_path string (";" for multiple paths supported)
-- @param filter string - eg.: ".txt"
-- @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)

  dir_path = dir_path or cwd
  filter = string.lower(filter) or "*"
  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

  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 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 string.lower(string.sub( f , - #filter ))==filter 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 · current revision
Edited December 10, 2012 9:32 pm GMT (diff)